]> granicus.if.org Git - python/commitdiff
bpo-35279: reduce default max_workers of ThreadPoolExecutor (GH-13618)
authorInada Naoki <songofacandy@gmail.com>
Tue, 28 May 2019 12:02:52 +0000 (21:02 +0900)
committerGitHub <noreply@github.com>
Tue, 28 May 2019 12:02:52 +0000 (21:02 +0900)
Doc/library/concurrent.futures.rst
Lib/concurrent/futures/thread.py
Lib/test/test_concurrent_futures.py
Misc/NEWS.d/next/Library/2019-05-28-19-14-29.bpo-35279.PX7yl9.rst [new file with mode: 0644]

index ffc29d782ec026ce5cd68a44032c9006d10bbcb4..f2491dd24571bec575fc8f2d80e2c7357497eecf 100644 (file)
@@ -159,6 +159,15 @@ And::
    .. versionchanged:: 3.7
       Added the *initializer* and *initargs* arguments.
 
+   .. versionchanged:: 3.8
+      Default value of *max_workers* is changed to ``min(32, os.cpu_count() + 4)``.
+      This default value preserves at least 5 workers for I/O bound tasks.
+      It utilizes at most 32 CPU cores for CPU bound tasks which release the GIL.
+      And it avoids using very large resources implicitly on many-core machines.
+
+      ThreadPoolExecutor now reuses idle worker threads before starting
+      *max_workers* worker threads too.
+
 
 .. _threadpoolexecutor-example:
 
index ad6b4c20b56681946b787be946bd87bad08734e1..2426e94de91fcb9115d13bea2707ac8e59472ad7 100644 (file)
@@ -129,9 +129,14 @@ class ThreadPoolExecutor(_base.Executor):
             initargs: A tuple of arguments to pass to the initializer.
         """
         if max_workers is None:
-            # Use this number because ThreadPoolExecutor is often
-            # used to overlap I/O instead of CPU work.
-            max_workers = (os.cpu_count() or 1) * 5
+            # ThreadPoolExecutor is often used to:
+            # * 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.
+            # 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)
         if max_workers <= 0:
             raise ValueError("max_workers must be greater than 0")
 
index de6ad8f2aa12028595d645aa52a5408400c9fb04..b27ae719482285ba9d7d84293afd0acba46f3db3 100644 (file)
@@ -755,8 +755,8 @@ class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, BaseTestCase):
 
     def test_default_workers(self):
         executor = self.executor_type()
-        self.assertEqual(executor._max_workers,
-                         (os.cpu_count() or 1) * 5)
+        expected = min(32, (os.cpu_count() or 1) + 4)
+        self.assertEqual(executor._max_workers, expected)
 
     def test_saturation(self):
         executor = self.executor_type(4)
diff --git a/Misc/NEWS.d/next/Library/2019-05-28-19-14-29.bpo-35279.PX7yl9.rst b/Misc/NEWS.d/next/Library/2019-05-28-19-14-29.bpo-35279.PX7yl9.rst
new file mode 100644 (file)
index 0000000..41ee5c2
--- /dev/null
@@ -0,0 +1,3 @@
+Change default *max_workers* of ``ThreadPoolExecutor`` from ``cpu_count() *
+5`` to ``min(32, cpu_count() + 4))``.  Previous value was unreasonably
+large on many cores machines.