Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/library/compileall.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ compile Python sources.
.. cmdoption:: -j N

Use *N* workers to compile the files within the given directory.
If ``0`` is used, then the result of :func:`os.cpu_count()`
If ``0`` is used, then the result of :func:`os.process_cpu_count()`
will be used.

.. cmdoption:: --invalidation-mode [timestamp|checked-hash|unchecked-hash]
Expand Down
6 changes: 5 additions & 1 deletion Doc/library/concurrent.futures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ And::
ThreadPoolExecutor now reuses idle worker threads before starting
*max_workers* worker threads too.

.. versionchanged:: 3.8
Default value of *max_workers* is changed to
``min(32, (os.process_cpu_count() or 1) + 4)``.


.. _threadpoolexecutor-example:

Expand Down Expand Up @@ -243,7 +247,7 @@ to a :class:`ProcessPoolExecutor` will result in deadlock.

An :class:`Executor` subclass that executes calls asynchronously using a pool
of at most *max_workers* processes. If *max_workers* is ``None`` or not
given, it will default to the number of processors on the machine.
given, it will default to :func:`os.process_cpu_count`.
If *max_workers* is less than or equal to ``0``, then a :exc:`ValueError`
will be raised.
On Windows, *max_workers* must be less than or equal to ``61``. If it is not
Expand Down
8 changes: 4 additions & 4 deletions Doc/library/multiprocessing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -996,13 +996,13 @@ Miscellaneous

This number is not equivalent to the number of CPUs the current process can
use. The number of usable CPUs can be obtained with
``len(os.sched_getaffinity(0))``
:func:`os.process_cpu_count`.

When the number of CPUs cannot be determined a :exc:`NotImplementedError`
is raised.

.. seealso::
:func:`os.cpu_count`
:func:`os.cpu_count` and :func:`os.process_cpu_count`

.. function:: current_process()

Expand Down Expand Up @@ -2214,7 +2214,7 @@ with the :class:`Pool` class.
callbacks and has a parallel map implementation.

*processes* is the number of worker processes to use. If *processes* is
``None`` then the number returned by :func:`os.cpu_count` is used.
``None`` then the number returned by :func:`os.process_cpu_count` is used.

If *initializer* is not ``None`` then each worker process will call
``initializer(*initargs)`` when it starts.
Expand Down Expand Up @@ -2775,7 +2775,7 @@ worker threads rather than worker processes.
:meth:`~multiprocessing.pool.Pool.terminate` manually.

*processes* is the number of worker threads to use. If *processes* is
``None`` then the number returned by :func:`os.cpu_count` is used.
``None`` then the number returned by :func:`os.process_cpu_count` is used.

If *initializer* is not ``None`` then each worker process will call
``initializer(*initargs)`` when it starts.
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ Other Language Changes
of the ``optimize`` argument.
(Contributed by Irit Katriel in :gh:`108113`).

* :mod:`multiprocessing`, :mod:`concurrent.futures`, :mod:`compileall`:
Replace :func:`os.cpu_count` with :func:`os.process_cpu_count` to select the
default number of worker threads and processes.
(Contributed by Victor Stinner in :gh:`109649`.)


New Modules
===========

Expand Down
2 changes: 1 addition & 1 deletion Lib/concurrent/futures/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ def __init__(self, max_workers=None, mp_context=None,
_check_system_limits()

if max_workers is None:
self._max_workers = os.cpu_count() or 1
self._max_workers = os.process_cpu_count() or 1
if sys.platform == 'win32':
self._max_workers = min(_MAX_WINDOWS_WORKERS,
self._max_workers)
Expand Down
4 changes: 2 additions & 2 deletions Lib/concurrent/futures/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,10 @@ def __init__(self, max_workers=None, thread_name_prefix='',
# * CPU bound task which releases GIL
# * I/O bound task (which releases GIL, of course)
#
# We use cpu_count + 4 for both types of tasks.
# We use process_cpu_count + 4 for both types of tasks.
# But we limit it to 32 to avoid consuming surprisingly large resource
# on many core machine.
max_workers = min(32, (os.cpu_count() or 1) + 4)
max_workers = min(32, (os.process_cpu_count() or 1) + 4)
if max_workers <= 0:
raise ValueError("max_workers must be greater than 0")

Expand Down
2 changes: 1 addition & 1 deletion Lib/multiprocessing/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def __init__(self, processes=None, initializer=None, initargs=(),
self._initargs = initargs

if processes is None:
processes = os.cpu_count() or 1
processes = os.process_cpu_count() or 1
if processes < 1:
raise ValueError("Number of processes must be at least 1")
if maxtasksperchild is not None:
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/libregrtest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
if self.num_workers < 0:
# Use all CPUs + 2 extra worker processes for tests
# that like to sleep
self.num_workers = (os.cpu_count() or 1) + 2
self.num_workers = (os.process_cpu_count() or 1) + 2

# For a partial run, we do not need to clutter the output.
if (self.want_header
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/libregrtest/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,9 @@ def display_header(use_resources: tuple[str, ...],

cpu_count = os.cpu_count()
if cpu_count:
process_cpu_count = os.process_cpu_count()
if process_cpu_count and process_cpu_count != cpu_count:
cpu_count = f"{process_cpu_count} (process) / {cpu_count} (system)"
print("== CPU count:", cpu_count)
print("== encodings: locale=%s, FS=%s"
% (locale.getencoding(), sys.getfilesystemencoding()))
Expand Down
1 change: 1 addition & 0 deletions Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def format_attr(attr, value):
'getresgid',
'getresuid',
'getuid',
'process_cpu_count',
'uname',
):
call_func(info_add, 'os.%s' % func, os, func)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_concurrent_futures/test_thread_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def record_finished(n):

def test_default_workers(self):
executor = self.executor_type()
expected = min(32, (os.cpu_count() or 1) + 4)
expected = min(32, (os.process_cpu_count() or 1) + 4)
self.assertEqual(executor._max_workers, expected)

def test_saturation(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`multiprocessing`, :mod:`concurrent.futures`, :mod:`compileall`:
Replace :func:`os.cpu_count` with :func:`os.process_cpu_count` to select the
default number of worker threads and processes. Patch by Victor Stinner.
2 changes: 1 addition & 1 deletion Modules/_decimal/tests/deccheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -1301,7 +1301,7 @@ def tfunc():
out, _ = p.communicate()
write_output(out, p.returncode)

N = os.cpu_count()
N = os.process_cpu_count()
t = N * [None]

for i in range(N):
Expand Down
2 changes: 1 addition & 1 deletion Tools/freeze/test/freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def prepare(script=None, outdir=None):
if not MAKE:
raise UnsupportedError('make')

cores = os.cpu_count()
cores = os.process_cpu_count()
if cores and cores >= 3:
# this test is most often run as part of the whole suite with a lot
# of other tests running in parallel, from 1-2 vCPU systems up to
Expand Down
5 changes: 4 additions & 1 deletion Tools/ssl/multissltests.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ class AbstractBuilder(object):
build_template = None
depend_target = None
install_target = 'install'
jobs = os.cpu_count()
if hasattr(os, 'process_cpu_count'):
jobs = os.process_cpu_count()
else:
jobs = os.cpu_count()

module_files = (
os.path.join(PYTHONROOT, "Modules/_ssl.c"),
Expand Down
6 changes: 5 additions & 1 deletion Tools/wasm/wasm_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,11 @@ def make_cmd(self) -> List[str]:
def getenv(self) -> Dict[str, Any]:
"""Generate environ dict for platform"""
env = os.environ.copy()
env.setdefault("MAKEFLAGS", f"-j{os.cpu_count()}")
if hasattr(os, 'process_cpu_count'):
cpu_count = os.process_cpu_count()
else:
cpu_count = os.cpu_count()
env.setdefault("MAKEFLAGS", f"-j{cpu_count}")
platenv = self.host.platform.getenv(self)
for key, value in platenv.items():
if value is None:
Expand Down