Skip to content

time.sleep (floatsleep()) should use clock_nanosleep() on Linux #65501

Closed

Description

@shankarunni
BPO 21302
Nosy @vstinner, @4kir4, @1st1, @eryksun, @Livius90
PRs
  • [3.10] bpo-21302: Add clock_nanosleep() implementation for time.sleep() in Linux #28077
  • bpo-21302: Add clock_nanosleep() implementation for time.sleep() #28111
  • bpo-21302: Update time.sleep() doc for clock_nanosleep() #28311
  • bpo-21302: Add Waitable timer implementation for time.sleep() in Windows #28341
  • bpo-21302: Add _PyTime_AsNanoseconds() #28350
  • bpo-21302: time.sleep() uses waitable timer on Windows #28483
  • bpo-21302: Add nanosleep() implementation for time.sleep() in Unix #28526
  • bpo-21302: Add nanosleep() implementation for time.sleep() in Unix #28545
  • Files
  • wait.py
  • bench.py
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2021-09-25.12:37:55.425>
    created_at = <Date 2014-04-18.20:12:03.043>
    labels = ['type-bug', 'library', '3.11']
    title = 'time.sleep (floatsleep()) should use clock_nanosleep() on Linux'
    updated_at = <Date 2021-10-11.08:30:28.782>
    user = 'https://bugs.python.org/shankarunni'

    bugs.python.org fields:

    activity = <Date 2021-10-11.08:30:28.782>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2021-09-25.12:37:55.425>
    closer = 'vstinner'
    components = ['Library (Lib)']
    creation = <Date 2014-04-18.20:12:03.043>
    creator = 'shankarunni'
    dependencies = []
    files = ['50289', '50294']
    hgrepos = []
    issue_num = 21302
    keywords = ['patch']
    message_count = 32.0
    messages = ['216799', '216814', '217225', '217226', '217227', '217233', '217235', '401108', '401701', '401706', '401824', '402279', '402437', '402438', '402440', '402441', '402442', '402448', '402618', '402619', '402630', '402632', '403528', '403544', '403550', '403568', '403569', '403581', '403583', '403610', '403632', '403633']
    nosy_count = 7.0
    nosy_names = ['vstinner', 'akira', 'python-dev', 'yselivanov', 'eryksun', 'shankarunni', 'Livius']
    pr_nums = ['28077', '28111', '28311', '28341', '28350', '28483', '28526', '28545']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue21302'
    versions = ['Python 3.11']

    Activity

    1. vstinner commented on Sep 22, 2021

      @vstinner
      Member

      New changeset 58f8adf by Victor Stinner in branch 'main':
      bpo-21302: time.sleep() uses waitable timer on Windows (GH-28483)
      58f8adf

    2. vstinner commented on Sep 22, 2021

      @vstinner
      Member

      Livius: your first PR modified Sleep() in Modules/_tkinter.c to use nanosleep(). I don't see the point since this function has a solution of 1 ms (10^-3). Using select() on Unix is enough: resolution of 1 us (10^-6).

    3. vstinner commented on Sep 22, 2021

      @vstinner
      Member

      bench.py: measure the shortest possible sleep. Use time.sleep(1e-10): 0.1 nanosecond. It should be rounded to the resolution of the used sleep function, like 1 ns on Linux.

      On Linux with Fedora 34 Python 3.10 executable, I get:

      Mean +- std dev: 60.5 us +- 12.9 us (80783 values)
      

      On Windows with a Python 3.11 debug build, I get:

      Mean +- std dev: 21.9 ms +- 7.8 ms (228 values)
      

      Sadly, it seems like on Windows 10, one of the following function still uses the infamous 15.6 ms resolution:

      • CreateWaitableTimerW()
      • SetWaitableTimer()
      • WaitForMultipleObjects()
    4. vstinner commented on Sep 22, 2021

      @vstinner
      Member

      On Windows with a Python 3.11 debug build, I get:
      Mean +- std dev: 21.9 ms +- 7.8 ms (228 values)

      I wrote an optimization to cache the Windows timer handle between time.sleep() calls (don't close it). I don't think that it's needed because they shortest sleep is about 15.6 ms. CreateWaitableTimerW() is likely way more fast than 15.6 ms. So this optimization is basically useless.

    5. vstinner commented on Sep 22, 2021

      @vstinner
      Member

      Livius: do you care about using nanosleep(), or can I close the issue?

    6. vstinner commented on Sep 22, 2021

      @vstinner
      Member

      See also bpo-19007: "precise time.time() under Windows 8: use GetSystemTimePreciseAsFileTime".

    7. vstinner commented on Sep 25, 2021

      @vstinner
      Member

      New changeset 7834ff2 by Victor Stinner in branch 'main':
      bpo-21302: Add nanosleep() implementation for time.sleep() in Unix (GH-28545)
      7834ff2

    8. vstinner commented on Sep 25, 2021

      @vstinner
      Member

      Thanks Livius for all these nice enhancements!

    9. Livius90 commented on Sep 25, 2021

      Livius90mannequin
      Mannequin

      Do you have any information about when will be it released in 3.11?

    10. vstinner commented on Sep 25, 2021

      @vstinner
      Member

      Do you have any information about when will be it released in 3.11?

      Here is a schedule of Python 3.11 releases:
      https://www.python.org/dev/peps/pep-0664/

      In the meanwhile, you can develop a C extension to get the feature.

    11. Livius90 commented on Oct 9, 2021

      Livius90mannequin
      Mannequin

      https://www.python.org/downloads/windows/
      "Note that Python 3.10.0 cannot be used on Windows 7 or earlier."

      vstinner: Is it true that Windows 7 is not supported OS anymore? In this case we do not need to care about Windows 7 and earlier Windows OS compatibility and it is time to use nicely GetSystemTimePreciseAsFileTime() in time.time() and time.sleep() as absolute sleeping because it is available since Windows 8.

    12. vstinner commented on Oct 9, 2021

      @vstinner
      Member

      it is time to use nicely GetSystemTimePreciseAsFileTime() in time.time()

      See bpo-19007 for that.

      it is time to (...) time.sleep() as absolute sleeping because it is available since Windows 8.

      In Python 3.11, time.sleep() is now always implemented with a waitable timer. I chose to use a relative timeout since it's simpler to implement. Is there any benefit of calling SetWaitableTimer() with an absolute timeout, compared to calling it with a relative timeout?

    13. eryksun commented on Oct 9, 2021

      @eryksun
      Contributor

      In Python 3.11, time.sleep() is now always implemented with a
      waitable timer.

      A regular waitable timer in Windows becomes signaled with the same resolution as Sleep(). It's based on the current interrupt timer period, which can be lowered to 1 ms via timeBeginPeriod(). Compared to Sleep() it's more flexible in terms of periodic waits, WaitForMultipleObjects(), or MsgWaitForMultipleObjects() -- not that time.sleep() needs this flexibility.

      That said, using a waitable timer leaves the door open for improvement in future versions of Python. In particular, it's possible to get higher resolution in newer versions of Windows 10 and Windows 11 with CreateWaitableTimerExW() and the undocumented flag CREATE_WAITABLE_TIMER_HIGH_RESOLUTION (2).

    14. Livius90 commented on Oct 10, 2021

      Livius90mannequin
      Mannequin

      Absolute timeout implementation via SetWaitableTimer() and GetSystemTimePreciseAsFileTime() is always better because it can reduce the "waste time" or "overhead time" what is always exist in any simple interval sleep implementation. Moreover, it is the only one which is eqvivalent with clock_nanosleep() implementation of Linux which is now the most state of the art implementation for precise sleeping.

      So, my opinion is that absolute timeout implementation could be the most modern and sustainable for future python.

    15. eryksun commented on Oct 10, 2021

      @eryksun
      Contributor

      Is there any benefit of calling SetWaitableTimer() with an
      absolute timeout

      No, the due time of a timer object is stored in absolute interrupt time, not absolute system time. This has to be calculated either way, and it's actually more work for the kernel if an absolute system time is passed.

    16. Livius90 commented on Oct 10, 2021

      Livius90mannequin
      Mannequin

      It is not true that there are no benefits. Absolute timeout using can reduce the overhead time of any variable and object intialization cost before the WaitForMultipleObjects() which will perform the real sleeping via blocking wait in pysleep(). GetSystemTimePreciseAsFileTime() must be call at the first line as much as it can in pysleep(). This is the same implementation in Linux via clock_nanosleep().

      So, to using absolute timeout and GetSystemTimePreciseAsFileTime() can improves the accuracy of the desired sleep time. For example if sleep = 2.0 sec then real relative sleep time = 2.001234 sec, but absolute sleep time = 2.000012 sec.

      Benefits are in not the technicaly backgorund, rather it is in the usecase.

    17. Livius90 commented on Oct 10, 2021

      Livius90mannequin
      Mannequin

      In other words, using absolute timeout can eliminate the systematic error of desired sleep time.

    18. eryksun commented on Oct 10, 2021

      @eryksun
      Contributor

      Absolute timeout using can reduce the overhead time of any variable
      and object intialization cost before the WaitForMultipleObjects()

      Again, timer objects store the due time in interrupt time, not system time (i.e. InterruptTime vs SystemTime in the KUSER_SHARED_DATA record). The due time gets set as the current interrupt time plus a relative due time. If the due time is passed as absolute system time, the kernel just computes the delta from the current system time.

      The timer object does record whether the requested due time is an absolute system time. This allows the kernel to recompute all absolute due times when the system time is changed manually. This is also the primary reason one wouldn't implement time.sleep() with absolute system time.

      using absolute timeout and GetSystemTimePreciseAsFileTime() can
      improves the accuracy of the desired sleep time.

      It would not improve the resolution. Timer objects are signaled when their due time is at or before the current interrupt time. The latter gets updated by the timer interrupt service routine, by default every 15.625 ms -- or at least that used to be the case.

      The undocumented flag CREATE_WAITABLE_TIMER_HIGH_RESOLUTION creates a different timer type, called an "IRTimer" (implemented in Windows 8.1, but back then only accessible in the NT API). This timer type is based on precise interrupt time, which is interpolated using the performance counter. I don't know how the implementation of the timer interrupt has changed to support this increased resolution. It could be that the default 15.625 ms interrupt period is being simulated for compatibility with classic timers and Sleep(). I'd love for the CREATE_WAITABLE_TIMER_HIGH_RESOLUTION flag and the behavior of IRTimer objects to be documented.

    19. vstinner commented on Oct 11, 2021

      @vstinner
      Member

      That said, using a waitable timer leaves the door open for improvement in future versions of Python. In particular, it's possible to get higher resolution in newer versions of Windows 10 and Windows 11 with CreateWaitableTimerExW() and the undocumented flag CREATE_WAITABLE_TIMER_HIGH_RESOLUTION (2).

      I created bpo-45429 "[Windows] time.sleep() should use CREATE_WAITABLE_TIMER_HIGH_RESOLUTION".

    20. vstinner commented on Oct 11, 2021

      @vstinner
      Member

      This issue is closed. If you consider that time.sleep() has a bug or could be enhanced, please open a new issue.

    21. transferred this issue fromon Apr 10, 2022
    22. Livius90 commented on Nov 12, 2022

      @Livius90
      Contributor

      Just for some high-lights, i did some test-run with bench.py:

      Linux - i.MX7D ARM

      Python 3.8
      root@myARM:~# python3 bench.py
      time.sleep(0.001) benchmark...
      Mean +- std dev: 1.1 ms +- 22.5 us (26525 values)
      
      root@myARM:~# python3 bench.py
      time.sleep(0.0001) benchmark...
      Mean +- std dev: 227.1 us +- 18.6 us (125174 values)
      Python 3.11
      root@myARM:~# python3 bench.py
      time.sleep(0.001) benchmark...
      Mean +- std dev: 1.1 ms +- 6.4 us (27686 values)
      
      root@myARM:~# python3 bench.py
      time.sleep(0.0001) benchmark...
      Mean +- std dev: 170.2 us +- 5.7 us (164893 values)

      Windows 10 - i7-10850H PC

      Python 3.10
      time.sleep(0.001) benchmark...
      Mean +- std dev: 15.9 ms +- 350.9 us (1889 values)
      
      time.sleep(0.0001) benchmark...
      Mean +- std dev: 15.9 ms +- 341.1 us (1888 values)
      Python 3.11
      time.sleep(0.001) benchmark...
      Mean +- std dev: 1.6 ms +- 339.6 us (19180 values)
      
      time.sleep(0.0001) benchmark...
      Mean +- std dev: 574.6 us +- 95.2 us (51837 values)
    23. gpshead commented on Dec 1, 2023

      @gpshead
      Member

      While I'm assuming this has to do with something we'll discover on our internal production platforms at work... is anyone else seeing time.sleep hang when you've compiled your 3.11 runtime such that it uses clock_nanosleep() instead of the old original select() code?

    24. vstinner commented on Dec 1, 2023

      @vstinner
      Member

      While I'm assuming this has to do with something we'll discover on our internal production platforms at work... is anyone else seeing time.sleep hang when you've compiled your 3.11 runtime such that it uses clock_nanosleep() instead of the old original select() code?

      Python calls clock_nanosleep() on clock CLOCK_MONOTONIC. It computes the absolute time using _PyTime_GetMonotonicClockWithInfo(). On Linux, _PyTime_GetMonotonicClockWithInfo() should be clock_gettime(CLOCK_MONOTONIC). It should be the same clock.

      Maybe check that both code paths use the same clock?

    25. gpshead commented on Dec 2, 2023

      @gpshead
      Member

      we found a broken CLOCK_MONOTONIC in some environments. we've worked around it until that gets fixed.

    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

      3.11only security fixesstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions