From b68232c05a37a617350e9462e2e7e9055f1bfce7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 11 Sep 2023 23:49:37 +0200 Subject: [PATCH] gh-109276: libregrtest: limit number workers Don't spawn more threads than the number of jobs: these worker threads would never get anything to do. * Add the number of tests in "Run ... tests in ..." message. * Add RunTests.get_jobs() method. * Add plural() function. * count() uses f-string. --- Lib/test/libregrtest/main.py | 7 ++++++- Lib/test/libregrtest/run_workers.py | 18 ++++++++++++++++-- Lib/test/libregrtest/runtests.py | 7 +++++++ Lib/test/libregrtest/utils.py | 13 +++++++++++-- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 2c0a6c204373cc..9cb0cfe0071a2a 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -292,7 +292,12 @@ def run_tests_sequentially(self, runtests): save_modules = sys.modules.keys() - msg = "Run tests sequentially" + jobs = runtests.get_jobs() + if jobs is not None: + tests = f'{jobs} tests' + else: + tests = 'tests' + msg = f"Run {tests} sequentially" if runtests.timeout: msg += " (timeout: %s)" % format_duration(runtests.timeout) self.log(msg) diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index 5c665abfeb57bd..cfa36f7800943a 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -21,7 +21,7 @@ from .single import PROGRESS_MIN_TIME from .utils import ( StrPath, StrJSON, TestName, MS_WINDOWS, - format_duration, print_warning) + format_duration, print_warning, plural) from .worker import create_worker_process, USE_PROCESS_GROUP if MS_WINDOWS: @@ -401,10 +401,24 @@ def __init__(self, num_workers: int, runtests: RunTests, self.worker_timeout = None self.workers = None + jobs = self.runtests.get_jobs() + if jobs is not None: + # Don't spawn more threads than the number of jobs: + # these worker threads would never get anything to do. + self.num_workers = min(self.num_workers, jobs) + def start_workers(self) -> None: self.workers = [WorkerThread(index, self) for index in range(1, self.num_workers + 1)] - msg = f"Run tests in parallel using {len(self.workers)} child processes" + jobs = self.runtests.get_jobs() + if jobs is not None: + tests = f'{jobs} tests' + else: + tests = 'tests' + nworkers = len(self.workers) + processes = plural(nworkers, "process", "processes") + msg = (f"Run {tests} in parallel using " + f"{nworkers} worker {processes}") if self.timeout: msg += (" (timeout: %s, worker timeout: %s)" % (format_duration(self.timeout), diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index 64f8f6ab0ff305..167cad6dbe1584 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -51,6 +51,13 @@ def get_match_tests(self, test_name) -> FilterTuple | None: else: return None + def get_jobs(self): + # Number of run_single_test() calls needed to run all tests. + # None means that there is not bound limit (--forever option). + if self.forever: + return None + return len(self.tests) + def iter_tests(self): if self.forever: while True: diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index b46cec6f0eec70..ce1b1088127bf7 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -70,11 +70,20 @@ def strip_py_suffix(names: list[str]): names[idx] = basename +def plural(n, singular, plural=None): + if n == 1: + return singular + elif plural is not None: + return plural + else: + return singular + 's' + + def count(n, word): if n == 1: - return "%d %s" % (n, word) + return f"{n} {word}" else: - return "%d %ss" % (n, word) + return f"{n} {word}s" def printlist(x, width=70, indent=4, file=None):