Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-109649: Add affinity parameter to os.cpu_count()#109652

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
vstinner wants to merge1 commit intopython:mainfromvstinner:cpu_count_usable

Conversation

vstinner
Copy link
Member

@vstinnervstinner commentedSep 21, 2023
edited
Loading

Implement cpu_count(affinity=True) with sched_getaffinity() on Unix
and GetProcessAffinityMask() on Windows.

Changes:

  • Fix test_posix.test_sched_getaffinity(): restore the old CPU mask
    when the test completes!
  • Doc: Specify that os.cpu_count() countslogicial CPUs and mention
    that Linux cgroups are ignored.
  • _Py_popcount32() uses UINT32_C() for M1, M2 and M4 constants.
  • Add _Py_popcount64(). Add tests on _Py_popcount64().

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


Return the number of CPUs in the system. Returns ``None`` if undetermined.
Return the number of logical CPUs in the system. Returns ``None`` if
Copy link
Member

@corona10corona10Sep 21, 2023
edited
Loading

Choose a reason for hiding this comment

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

You have to update the documentation that it will return the number of logical CPUs for theprocess if the usable is True.
Because the current os.cpu_count returns the available CPU counts from the system not the process.
It's different layer.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

The doc says:

Ifaffinity is true, return the number of logical CPUs the current process can use.

It's not clear enough? Do you want to propose a different phrasing?

Copy link
Member

Choose a reason for hiding this comment

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

That is reasonable.

I'd add "this may be less than the number of logical cpus returned by affinity=False due to OS or container limitations imposed upon the process" to make it more clearwhy people should want to use theaffinity=True argument.

PS thanks for making it keyword only!

I do wish this API never used the term "cpu"... everything these days is really a "logical_core" and what that even means depends a lot on underlying infrastructure and platform that Python may not be able to introspect. Way too late for that though. :)

Copy link
MemberAuthor

@vstinnervstinnerSep 21, 2023
edited
Loading

Choose a reason for hiding this comment

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

My PR adds "logical CPU" to the doc. In previous bug reports, I saw some confusion between physical CPU core, CPU packages, CPU threads, and logical CPUs.

gpshead reacted with thumbs up emoji
@vstinnervstinner changed the titlegh-109649: Add usable parameter to os.cpu_count()gh-109649: Add affinity parameter to os.cpu_count()Sep 21, 2023
@vstinner
Copy link
MemberAuthor

I updated my PR:

  • Renameusable parameter toaffinity.
  • Add Windows implementation using GetProcessAffinityMask().
  • Add _Py_popcount64().

@vstinner
Copy link
MemberAuthor

I don't think that my Windows implementation works with more than 64 CPUs :-(

https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessaffinitymask#return-value

It should be fixed before this PR can be merged.

@vstinner
Copy link
MemberAuthor

Manual test on Windows,affinity.py:

importoswithopen("output","w")asfp:try:print(os.cpu_count(affinity=True),file=fp)exceptExceptionasexc:print(f"ERROR:{exc!r}",file=fp)

Without CPU affinity (all CPUs):

vstinner@WIN C:\victor\python\main>PCbuild\amd64\python_d.exe affinity.py                    vstinner@WIN C:\victor\python\main>type output2

With CPU affinity (limit to 1 CPU):

vstinner@WIN C:\victor\python\main>start /affinity 01 PCbuild\amd64\python_d.exe affinity.pyvstinner@WIN C:\victor\python\main>type output1

@vstinner
Copy link
MemberAuthor

cc@taleinat

@vstinnervstinner marked this pull request as draftSeptember 21, 2023 21:30
@vstinner
Copy link
MemberAuthor

I mark the PR as a draft to remind myself that either I should drop Windows support for now, or I should fix Windows support, before this PR can be considered ready to be reviewed (and then merged).

As I wrote previous, my current implementation is limited to 64 CPUs on Windows which looks wrong. A Windows machinecan have more than 64 CPUs:#67226 But I'm not sure if a process can be assigned to more than 64 CPUs. Well, I have to investigate :-)

gpshead reacted with thumbs up emojigpshead reacted with heart emoji


Linux control groups, *cgroups*, are not taken in account to get the number
Copy link
Member

Choose a reason for hiding this comment

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

I feel like people wanting such an API may also want cgroups to also be considered when the real question being answered is really"How parallel am I usefully allowed to be?".

Does that need to be separated out into its owncgroups_cpuset=True flag so that people could query one or the other or both? The use cases I have in mind are all around the above question where I'd always want the combination akamin(logical_cpus, affinity_cores, cgroups_cpuset_cores).

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

My PR just fix the documentation to avoid any misunderstading.

I feel like people wanting such an API may also want cgroups to also be considered

It is discussed in PR#80235. So far, nobody proposes any PR to implement this.

Maybe this PR is a baby step forward :-)

Copy link
Member

Choose a reason for hiding this comment

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

Even if we decide to supportcgroup in the future, I would like to propose not to use the flag name that can represent the implementation detail. If some platform suggests new things do we have to add a new flag for them?

gpshead reacted with thumbs up emoji
@gpsheadgpshead self-assigned thisSep 21, 2023
Implement cpu_count(affinity=True) with sched_getaffinity().Changes:* Fix test_posix.test_sched_getaffinity(): restore the old CPU mask  when the test completes!* Doc: Specify that os.cpu_count() counts *logicial* CPUs. Mention  that Linux cgroups are ignored.
@vstinnervstinner marked this pull request as ready for reviewSeptember 21, 2023 23:56
@vstinner
Copy link
MemberAuthor

I updated my PR. It's now ready for review.

Changes:

  • Remove Windows implementation.
  • Test that os.cpu_count(affinity=True) <= os.cpu_count(affinity=False).
  • I reverted my minor test change when cpu_count() returns None.
  • The doc no longer announces that os.cpu_count(affinity=True) raises an exception on error, since it falls back on os.cpu_count(affinity=False) code path is os.sched_getaffinity() is not available. It may be rephrased later when the feature will be implemented for Windows.

I mark the PR as a draft to remind myself that either I should drop Windows support for now, or I should fix Windows support, before this PR can be considered ready to be reviewed (and then merged).

I chose the easy way: remove the Windows implementation for now.

@vstinner
Copy link
MemberAuthor

vstinner commentedSep 22, 2023
edited
Loading

Honestly, IMO cpu_count(affinity=True) is what most users expect by default: decide how many workers (threads or CPUs) should be spawn to maximize efficiency without killing performances. If a server has 100 CPUs but Python is limited by the admin to 2 CPUS, spawning 100 worker processes is likely to kill latency and may cause many timeout issues.

Problem: changing the default behavior is wrong for different reasons. For example, a program can simply query cpu_count() to display how many logical CPUs a machine has. It doesn't matter if this program is limited to 1 CPUs or has access to all 100 CPUs of the server. It should always display 100.

PYTHONCPUCOUNT env var and -X cpu_count cmdline option sounds complementary with my change:
#109595 (comment)

If we can use PYTHONCPUCOUNT=affinity / -X cpu_count=affinity, it would fit into the first use case (spawn worker processes). It will break the second use case which will have to modify their code to use cpu_count(affinity=False). But it's different: someone sets the env var knowing consequences.

gpshead reacted with thumbs up emoji

@vstinner
Copy link
MemberAuthor

@gpshead@corona10: So, are you ok with this change? Do you think that we can continue this approach later to maybe addcgroups parameter (if it makes sense and if it is needed)? This approach fits with issuegh-109595 design, no?

corona10 reacted with thumbs up emoji

@corona10
Copy link
Member

corona10 commentedSep 26, 2023
edited
Loading

So, are you ok with this change?

No objection, it will need to people who get cpu_count with affinity option :)

@gpshead
Copy link
Member

Same, lets do this. I'm even okay with an envvar + -X option to override the return value after our discussions (followon PR i'd assume). And if Windows only supports a return value of up to 64 for the affinity feature (that came up in one of these discussions or PRs iirc?), just document the caveat while we get guidance from windows experts on how to get a better answer there.

@vstinnervstinner marked this pull request as draftSeptember 26, 2023 15:39
@vstinner
Copy link
MemberAuthor

I marked again my PR as a draft after a discussion with@corona10. I'm sorry about this back and forth.

It seems like there is a misunderstanding about "system CPU count" and "process CPU count". Depending on the use case, you may pick one or the other. The problem is that if you consider-X cpu_count=4 option: which value should be overriden? System CPU count or process CPU count? It becomes very blurry.

So.

I created PRgh-109907 which adds anew os.process_cpu_count(). The os.cpu_count() stays unchanged: it sticks to its documentation, it returns the number of logical CPUsof the system.

  • process_cpu_count(): currently gets sched_getaffinity(), but later it may read cgroups and be affected by-X cpu_count=value option.
  • cpu_count(): unchanged, number of system logical CPUs.

I understood that@corona10 is unhappy about this proposition because he wants to overriden the CPU count of Python applications which currently useos.cpu_count(). I understand this use case, but there are other ways like injecting asitecustomize scripts to get a new PYTHONCPUCOUNT variable which would simply replace the whole function:

Trysitecustomize.py:

importosncpu=os.environ.get('PYTHONCPUCOUNT',None)ifncpu:try:ncpu=int(ncpu)exceptValueError:print(f"WARNING: invalid PYTHONCPUCOUNT value:{ncpu!r}")else:defcpu_count():returnncpucpu_count.__doc__=os.cpu_count.__doc__os.cpu_count=cpu_count

Example:

vstinner@mona$ PYTHONPATH=$PWD ./python -c "import os; print(os.cpu_count())"12vstinner@mona$ PYTHONCPUCOUNT=4096 PYTHONPATH=$PWD ./python -c "import os; print(os.cpu_count())"4096vstinner@mona$ PYTHONCPUCOUNT=xxx PYTHONPATH=$PWD ./python -c "import os; print(os.cpu_count())"WARNING: invalid PYTHONCPUCOUNT value: 'xxx'12

Then put yoursitecustomize.py somewhere in one ofsys.path directories. It also works with a magic.pth file I suppose.

@corona10
Copy link
Member

Then put your sitecustomize.py somewhere in one of sys.path directories. It also works with a magic .pth file I suppose.

No, it doesn't solve the issue what I reported, How you can inject the customer's sys.path that is already written Docker image file? User will just pull the docker image from the already stored docker container image store. From the K8S admin there is no way to control CPU count except cmdline or environment variable.

@gpshead
Copy link
Member

The problem is that if you consider-X cpu_count=4 option: which value should be overridden? System CPU count or process CPU count? It becomes very blurry.

When a -X or equivalent environment variable is encountered, overrideall return values fromos.cpu_count(). That potential feature can be documented as the user explicitly asking our API to ignore whatever the OS APIs might have said.

@gpshead
Copy link
Member

Realize that no matter what, these new features don't solve anyones existing problems because they're having those problems on Python 3.12 and earlier which will never see these features. They already need to concoct workarounds (which are still going to work in 3.13+).

Adding anaffinity= parameteror adding a newos.process_cpu_count() API is useful as a featureregardless of any possible future plans for ways to override the return values here.

@vstinner
Copy link
MemberAuthor

Closed in favor of adding os.process_cpu_count() instead: PRgh-109907.

@vstinnervstinner deleted the cpu_count_usable branchSeptember 30, 2023 21:35
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@gpsheadgpsheadgpshead left review comments

@aiskaiskaisk left review comments

@corona10corona10corona10 left review comments

Assignees

@gpsheadgpshead

Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

4 participants
@vstinner@corona10@gpshead@aisk

[8]ページ先頭

©2009-2025 Movatter.jp