GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-109649: Add os.process_cpu_count() function by vstinner · Pull Request #109907 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Sep 26, 2023 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member
  • Fix test_posix.test_sched_getaffinity(): restore the old CPU mask when the test completes!
  • Doc: Specify that os.cpu_count() counts logicial CPUs.
  • Issue: os.process_cpu_count(): get the number CPUs usable by the current process #109649

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added the awaiting review label Sep 26, 2023
@bedevere-app bedevere-app Bot mentioned this pull request Sep 21, 2023
os.process_cpu_count(): get the number CPUs usable by the current process #109649
Closed
@vstinner vstinner mentioned this pull request Sep 26, 2023
gh-109649: Add affinity parameter to os.cpu_count() #109652
Closed
@vstinner
vstinner force-pushed the process_cpu_count branch from e3554fa to 69c8bf4 Compare September 26, 2023 15:52
@vstinner

vstinner commented Sep 26, 2023

Copy link
Copy Markdown
Member Author

See previous discussions:

  • issue os.process_cpu_count(): get the number CPUs usable by the current process #109649
  • issue Support python -Xcpu_count=<n> feature for container environment. #109595
  • PR gh-109595: Add -Xcpu_count=<n> cmdline for container users #109667
  • PR gh-109649: Add affinity parameter to os.cpu_count() #109652
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 26, 2023

Copy link
Copy Markdown
Member Author

Adding a new process_cpu_count() makes it easier to extend the function later for new use cases, instead of modifying the existing os.cpu_count():

  • Maybe read Linux cgroups
  • Maybe add a pid parameter to get the CPU count of another process.
  • -X cpu_count=value option
  • Future new exciting stuff :-)

About the pid parameter. First, I would like to implement this function on Windows (get process affinity), and then check if it would be possible to get the CPU affinity of another process on Windows, before considering to add a pid parameter.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 26, 2023

Copy link
Copy Markdown
Member Author

In psutil, this function is called Process.cpu_affinity(): https://psutil.readthedocs.io/en/latest/#psutil.Process.cpu_affinity

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
vstinner marked this pull request as draft September 27, 2023 00:41
@bedevere-app bedevere-app Bot removed the awaiting review label Sep 27, 2023
@vstinner
vstinner force-pushed the process_cpu_count branch from 69c8bf4 to 7832caa Compare September 29, 2023 13:32
@vstinner
vstinner marked this pull request as ready for review September 29, 2023 13:33
@bedevere-app bedevere-app Bot added the awaiting review label Sep 29, 2023
@vstinner

vstinner commented Sep 29, 2023

Copy link
Copy Markdown
Member Author

I prefer adding a new os.process_cpu_count() function, instead of adding a process=True parameter to the existing os.cpu_count() function because:

  • I consider adding a pid optional argument to os.process_cpu_count()
  • IMO the word "process" makes it explicit that the result is specific to the process, wheras cpu_count() is system-wide
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner mentioned this pull request Sep 29, 2023
Support python -Xcpu_count=<n> feature for container environment. #109595
Closed
@corona10
corona10 self-requested a review September 29, 2023 15:18
@corona10

corona10 commented Sep 29, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

I will take a look at it by tomorrow!

vstinner reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 29, 2023

Copy link
Copy Markdown
Member Author

cc @gpshead @indygreg

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead
gpshead reviewed Sep 29, 2023
View reviewed changes
Comment thread Modules/posixmodule.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 29, 2023

Copy link
Copy Markdown
Member Author

@gpshead: I updated my PR to reimplement os.process_cpu_count() in Python.

gpshead reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead
gpshead reviewed Sep 30, 2023
View reviewed changes
Comment thread Lib/os.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 30, 2023

Copy link
Copy Markdown
Member Author
cpu_set = sched_getaffinity(0)
num_cpus = cpu_count()
return min(len(cpu_set), num_cpus) if cpu_set else num_cpus

Why do you want to return cpu_count() if it's smaller than len(sched_getaffinity(0))? It should not happen. If it happens, I would prefer to not workaround bugs, but fix the OS instead. For me, it should not happen.

sched_getaffinity() is basically a "mask" on the list of all available CPUs. It cannot be larger, unless cpu_count() ... counts CPUs differently. In that case, we should fix cpu_count() instead, no?

When I added time.monotonic(), I added an assertion in debug mode to make sure that... the clock is monotonic (doesn't go backward). Surprise, surprise. The clock is not. On some virtual machines, I saw it jumping backwards sometimes. Honestly. I just removed the assertion. Python cannot and should not work around OS bugs, but just expose them. Maybe for the worst known OS bugs, we might detect them and issue a warning. But I'm not even sure if it's worth it.

I recall that Python detects broken poll() implementation on macOS. In that case, we go further: we remove the function at runtime! (at Python startup)

I prefer to not make assumptions about hypothetical bugs, but wait until we get real concrete bug reports, and then decide how to deal with them.

Anyway, thanks for thinking about all corner cases, it's a good thing!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@corona10

corona10 commented Sep 30, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Why do you want to return cpu_count() if it's smaller than len(sched_getaffinity(0))? It should not happen. If it happens, I would prefer to not workaround bugs, but fix the OS instead. For me, it should not happen

If we pass the -Xcpu_count=limited_number, it will be a more efficient implementation, we have to override os.cpu_count only. (It will automatically override os.process_cpu_count)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

corona10
corona10 approved these changes Sep 30, 2023
View reviewed changes

@corona10 corona10 left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Overall LGTM, for the detail, I will delegate it to @gpshead

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting core review and removed awaiting review labels Sep 30, 2023
@vstinner

vstinner commented Sep 30, 2023

Copy link
Copy Markdown
Member Author

If we pass the -Xcpu_count=limited_number, it will be a more efficient implementation, we have to override os.cpu_count only. (It will automatically override os.process_cpu_count)

I would prefer that the discussion separately the implementation and the expected behavior.

Also, can we discussion -Xcpu_count later? This PR is about adding process_cpu_count(), nothing more.

corona10 and gpshead reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead
gpshead reviewed Sep 30, 2023
View reviewed changes
Comment thread Doc/library/os.rst Outdated

.. function:: process_cpu_count()

Get the number of logical CPUs usable by the current process. Returns

@gpshead gpshead Sep 30, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

It is probably more accurate to say "usable by the calling thread of the current process". I believe each thread can have its own affinity.

(For parallelism planning purposes, the main thread prior to spawning others is likely what people would be calling this from anyways)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Sep 30, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Sadly, you're right. I updated the doc.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
gpshead
gpshead approved these changes Sep 30, 2023
View reviewed changes

@gpshead gpshead left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

One documentation improvement suggested but otherwise good.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner

vstinner commented Sep 30, 2023

Copy link
Copy Markdown
Member Author

@gpshead is right, the number of CPUs is "per thread" in thread.

Example:

import os
import threading

def sched_remove_one_cpu():
    mask = os.sched_getaffinity(0)
    mask.pop()
    os.sched_setaffinity(0, mask)

def set_affinity():
    tid = threading.get_native_id()
    print(f"thread {tid}: remove a second CPU")
    sched_remove_one_cpu()
    print(f"thread {tid}: {os.process_cpu_count()=}")

tid = threading.get_native_id()

print(f"main thread {tid}: remove a CPU")
sched_remove_one_cpu()

print(f"main thread {tid} before: {os.cpu_count()=}")
print(f"main thread {tid} before: {os.process_cpu_count()=}")

print()
thread = threading.Thread(target=set_affinity)
thread.start()
thread.join()
print()

print(f"main thread {tid} after: {os.cpu_count()=}")
print(f"main thread {tid} after: {os.process_cpu_count()=}")

Output on Linux:

main thread 2059677: remove a CPU
main thread 2059677 before: os.cpu_count()=12
main thread 2059677 before: os.process_cpu_count()=11

thread 2059678: remove a second CPU
thread 2059678: os.process_cpu_count()=10

main thread 2059677 after: os.cpu_count()=12
main thread 2059677 after: os.process_cpu_count()=11

You can see that the main thread loses a CPU when sched_remove_one_cpu() is called: os.process_cpu_count() is affected, but os.cpu_count() is not affected.

When a thread removes a second CPU, it affects os.process_cpu_count() in the thread, but the second CPU removal does not affected the main thread.

New thread inherit the mask of the caller thread, but a change in a child thread does not affect the parent thread. Well, that's the expected behavior.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead
gpshead reviewed Sep 30, 2023
View reviewed changes
Comment thread Doc/library/os.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
pythongh-109649: Add os.process_cpu_count() function
74ed3db
* Refactor os_sched_getaffinity_impl(): move variable definitions to
  their first assignment.
* Fix test_posix.test_sched_getaffinity(): restore the old CPU mask
  when the test completes!
* Doc: Specify that os.cpu_count() counts *logicial* CPUs.
* Doc: Specify that os.sched_getaffinity(0) is related to the calling
  thread.
@vstinner
vstinner force-pushed the process_cpu_count branch from 803f717 to 74ed3db Compare September 30, 2023 21:29
@vstinner

vstinner commented Sep 30, 2023

Copy link
Copy Markdown
Member Author

I made a few further changes to clarify differences between cpu_count(), process_cpu_count() and sched_affinity().

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Update docstring as well
9018c69
@vstinner

vstinner commented Sep 30, 2023

Copy link
Copy Markdown
Member Author

Tests / Hypothesis tests on Ubuntu (pull_request) Failing after 6m

FAIL: test_find_periodic_pattern (test.test_userstring.UserStringTest.test_find_periodic_pattern) (p='', text='')

I created issue GH-110160 for this bug.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 30, 2023

Copy link
Copy Markdown
Member Author

Unrelated CI failures:

  • Windows x86: FAIL: test_kbhit (test.test_msvcrt.TestConsoleIO.test_kbhit): test_msvcrt: test_getwche() failed with timeout (10 min) on GHA Windows x64 #110147 (comment)
  • Windows x86: FAIL: test_timeout (test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_timeout): test_multiprocessing_spawn: test_timeout() failed on GHA Windows x86 #110162
  • Hypothesis tests on Ubuntu: FAIL: test_find_periodic_pattern (test.test_userstring.UserStringTest.test_find_periodic_pattern) (p='', text=''): test_userstring: test_find_periodic_pattern() failed on GHA Hypothesis tests on Ubuntu #110160
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
vstinner merged commit c815210 into python:main Sep 30, 2023
@vstinner
vstinner deleted the process_cpu_count branch September 30, 2023 22:12
@bedevere-app bedevere-app Bot removed the awaiting core review label Sep 30, 2023
@vstinner

vstinner commented Sep 30, 2023

Copy link
Copy Markdown
Member Author

Merged, thanks for reviews @gpshead and @corona10!

gpshead reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot

bedevere-bot commented Sep 30, 2023

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot aarch64 RHEL8 3.x has failed when building commit c815210.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/all/#builders/529/builds/5071) and take a look at the build logs.
  4. Check if the failure is related to this commit (c815210) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/all/#builders/529/builds/5071

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-aarch64/build/Lib/threading.py", line 1066, in _bootstrap_inner
    self.run()
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-aarch64/build/Lib/threading.py", line 1003, in run
    self._target(*self._args, **self._kwargs)
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-aarch64/build/Lib/test/test_interpreters.py", line 484, in task
    interp = interpreters.create()
             ^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-aarch64/build/Lib/test/support/interpreters.py", line 25, in create
    id = _interpreters.create(isolated=isolated)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: interpreter creation failed
k


Traceback (most recent call last):
  File "<frozen importlib._bootstrap>", line 1354, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1325, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 929, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 1004, in exec_module
  File "<frozen importlib._bootstrap_external>", line 1100, in get_code
  File "<frozen importlib._bootstrap_external>", line 1199, in get_data
TypeError: descriptor 'close' for '_io.BufferedReader' objects doesn't apply to a '_io.FileIO' object
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gpshead

gpshead commented Sep 30, 2023

Copy link
Copy Markdown
Member

if you're inclined to do so, the documentation improvements to the existing APIs in this PR would be worthwhile backporting to the 3.12 branch so that they show up on the default /3/ docs on python.org soon.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Oct 1, 2023

Copy link
Copy Markdown
Member Author

@gpshead:

if you're inclined to do so, the documentation improvements to the existing APIs in this PR would be worthwhile backporting to the 3.12 branch so that they show up on the default /3/ docs on python.org soon.

It makes sense. I wrote PR gh-110169 for Python 3.12 (and added "backport to 3.11" label).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou encukou mentioned this pull request Mar 28, 2024
[doc] add platform availabity information for os.sched_getaffinity #88047
Closed
Glyphack pushed a commit to Glyphack/cpython that referenced this pull request Sep 2, 2024
@vstinner @Glyphack
pythongh-109649: Add os.process_cpu_count() function (python#109907)
7662bfc
* Refactor os_sched_getaffinity_impl(): move variable definitions to
  their first assignment.
* Fix test_posix.test_sched_getaffinity(): restore the old CPU mask
  when the test completes!
* Doc: Specify that os.cpu_count() counts *logicial* CPUs.
* Doc: Specify that os.sched_getaffinity(0) is related to the calling
  thread.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@gpshead gpshead gpshead approved these changes
@corona10 corona10 corona10 approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@vstinner @corona10 @bedevere-bot @gpshead
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.