Skip to content
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

gh-125997: suggest efficient alternatives for time.sleep(0) #128752

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

picnixz
Copy link
Member

@picnixz picnixz commented Jan 12, 2025

After a discussion with Victor and because #128274 is not convincing enough, we decide to:

  • keep the current behaviour of time.sleep(), and
  • document efficient (and probably semantically more correct) alternatives for time.sleep(0).

See #125997 (comment) for more details.


📚 Documentation preview 📚: https://cpython-previews--128752.org.readthedocs.build/

The implementation of `time.sleep()` changed in Python 3.11 and relies
on `clock_nanosleep()` or `nanosleep()` since then. This introduced a
regression in code using `time.sleep(0)` for a syscall "no-op", polling
or momentarily suspending the caller's thread.

To alleviate the performance regression, we suggest some alternatives
depending on the caller's needs.
@picnixz picnixz requested a review from vstinner January 12, 2025 18:48
@picnixz
Copy link
Member Author

picnixz commented Jan 12, 2025

@hauntsaninja and @charles-cooper: I would appreciate if you can share your thoughts on those suggestions as you were also involved in the other PR's discussion.

@charles-cooper
Copy link

i think it's interesting that select() is already mentioned as a possible implementation for time.sleep(), so maybe the approach in #128274 is valid actually.

https://github.com/python/cpython/pull/128752/files#diff-6203e0fc16084e67996c5bdd5e1caf4cc4699f67fde5da0abebe2ca239278ed4R402

besides that, i would just mention that clock_nanosleep may sleep at least 50us on linux. (i mean this is arguably a linux bug, but it is kind of surprising to users that sleep(0) sleeps a lot longer than one might expect).

@picnixz
Copy link
Member Author

picnixz commented Jan 12, 2025

sleep(0) sleeps a lot longer than one might expect

Actually, sleep() does not provide any guarantee that it will sleep exactly of the amount being requested. Granted it's surprising, but it's still documented in man nanosleep and others (sleep says "sleep() causes the calling thread to sleep either until the number of real-time seconds specified in seconds have elapsed" without saying "at least until" but I think it should be understood like this).

(), so maybe the approach in #128274 is valid actually.

While it's mentioned like that, I think it's wrong in the first place to rely on select() to implement sleep() (as I said on the issue). But since it's documented as a fallback, I think we should live with this discrepency, but we shouldn't make the "correct" implementation uses something else (namely "select()"). Even if the correct choice (clock_nanosleep/nanosleep) looks "buggy", that's what the caller should expect (I mean, I would expect time.sleep to rely on sleep-like functions).

Another solution is to check if we're on FreeBSD or not, but this makes maintainance harder. Also, for those reading the PR only, the sleep gap only happens at t = 0 and not at t = 1e-9 (for t = 1e-9, select() is still as slow as clock_nanosleep()). It's just that at t = 0, it returns immediately. So we can also just skip t = 0 calls on non-Windows platforms directly.

Copy link
Contributor

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this PR looks great, I think this is right approach.

I would not document current implementation choices of default Linux scheduler, especially since we've currently only had one report in >2 years. (If it does come up again, we can mention os.sched_setscheduler / PR_SET_TIMERSLACK)

.. note::

For polling, consider using :meth:`select.poll.poll(0) <select.poll.poll>`
instead of ``time.sleep(0)``. To emulate a "no-op", use :keyword:`pass`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that it's the right place to mention polling file descriptors in time.sleep() documentation.

For polling, consider using :meth:`select.poll.poll(0) <select.poll.poll>`
instead of ``time.sleep(0)``. To emulate a "no-op", use :keyword:`pass`.

To voluntarily relinquish the CPU, specify a read-time :ref:`scheduling
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read-time?

Copy link
Contributor

@hauntsaninja hauntsaninja Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To voluntarily relinquish the CPU, specify a read-time :ref:`scheduling
To voluntarily relinquish the CPU, specify a real-time :ref:`scheduling

Probably meant real time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting merge docs Documentation in the Doc dir skip news
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

5 participants