From 8c813faf864ac1d788a3efc45b4e76c1c3c3b340 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 12 Sep 2023 05:47:04 +0200 Subject: gh-109276: libregrtest: limit number workers (#109288) 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 f52deac..a89e3c6 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -295,7 +295,12 @@ class Regrtest: 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 5c665ab..cfa36f7 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -21,7 +21,7 @@ from .runtests import RunTests 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 @@ class RunWorkers: 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 656958f..5c68df1 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -53,6 +53,13 @@ class RunTests: 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 b46cec6..ce1b108 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): -- cgit v0.12