From 2d41d31f8120c8babc84e81dcd42bd7a975d8f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Kohlsdorf?= Date: Wed, 24 Feb 2021 16:39:58 -0300 Subject: [PATCH 1/5] perf(scheduler): sort scopes by number of tests to start with biggest scope first --- changelog/632.feature.rst | 1 + src/xdist/scheduler/loadscope.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog/632.feature.rst diff --git a/changelog/632.feature.rst b/changelog/632.feature.rst new file mode 100644 index 00000000..0e3b6e4d --- /dev/null +++ b/changelog/632.feature.rst @@ -0,0 +1 @@ +LoadScope scheduler: Sort scopes by number of tests to assign biggest scopes first. diff --git a/src/xdist/scheduler/loadscope.py b/src/xdist/scheduler/loadscope.py index 35a9ef34..76e17769 100644 --- a/src/xdist/scheduler/loadscope.py +++ b/src/xdist/scheduler/loadscope.py @@ -350,11 +350,18 @@ def schedule(self): return # Determine chunks of work (scopes) + unsorted_workqueue = OrderedDict() for nodeid in self.collection: scope = self._split_scope(nodeid) - work_unit = self.workqueue.setdefault(scope, default=OrderedDict()) + work_unit = unsorted_workqueue.setdefault(scope, default=OrderedDict()) work_unit[nodeid] = False + # Insert tests scopes into work queue ordered by number of tests + for scope, nodeids in sorted( + unsorted_workqueue.items(), key=lambda item: -len(item[1]) + ): + self.workqueue[scope] = nodeids + # Avoid having more workers than work extra_nodes = len(self.nodes) - len(self.workqueue) From 1b0334bab9c8dfe0fdf05ce0266b8088ce35b729 Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Thu, 21 Apr 2022 19:36:29 +0100 Subject: [PATCH 2/5] Test workqueue is ordered by size --- testing/acceptance_test.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 60edd9cc..7b83f021 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1232,6 +1232,22 @@ def test(self, i): "test_a.py::TestB", result.outlines ) in ({"gw0": 10}, {"gw1": 10}) + def test_workqueue_ordered_by_size(self, pytester: pytest.Pytester) -> None: + test_file = """ + import pytest + @pytest.mark.parametrize('i', range({})) + def test(i): + pass + """ + pytester.makepyfile(test_a=test_file.format(10), test_b=test_file.format(20)) + result = pytester.runpytest("-n2", "--dist=loadscope", "-v") + assert get_workers_and_test_count_by_prefix( + "test_a.py::test", result.outlines + ) == {"gw1": 10} + assert get_workers_and_test_count_by_prefix( + "test_b.py::test", result.outlines + ) == {"gw0": 20} + def test_module_single_start(self, pytester: pytest.Pytester) -> None: """Fix test suite never finishing in case all workers start with a single test (#277).""" test_file1 = """ From 867528f53438b5416d46ebeec60f8533382dfa04 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 21 Nov 2023 11:24:46 -0300 Subject: [PATCH 3/5] Update 632.feature.rst --- changelog/632.feature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/632.feature.rst b/changelog/632.feature.rst index 0e3b6e4d..ad5854bd 100644 --- a/changelog/632.feature.rst +++ b/changelog/632.feature.rst @@ -1 +1 @@ -LoadScope scheduler: Sort scopes by number of tests to assign biggest scopes first. +``--dist=loadscope`` now sorts scopes by number of tests to assign largets scopes early -- in many cases this should improve overall test session running time, as there is less chance of a large scope being left to be processed near the end of the session, leaving other workers idle. From 7bd858e110a533c96d2b99668e66e027ba384769 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 21 Nov 2023 11:25:10 -0300 Subject: [PATCH 4/5] Update src/xdist/scheduler/loadscope.py --- src/xdist/scheduler/loadscope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xdist/scheduler/loadscope.py b/src/xdist/scheduler/loadscope.py index 76e17769..bcfe11fe 100644 --- a/src/xdist/scheduler/loadscope.py +++ b/src/xdist/scheduler/loadscope.py @@ -356,7 +356,7 @@ def schedule(self): work_unit = unsorted_workqueue.setdefault(scope, default=OrderedDict()) work_unit[nodeid] = False - # Insert tests scopes into work queue ordered by number of tests + # Insert tests scopes into work queue ordered by number of tests. for scope, nodeids in sorted( unsorted_workqueue.items(), key=lambda item: -len(item[1]) ): From d7f60ec122a3c0349800f199f69148c6ff828ce9 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 21 Nov 2023 11:27:11 -0300 Subject: [PATCH 5/5] Update 632.feature.rst --- changelog/632.feature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/632.feature.rst b/changelog/632.feature.rst index ad5854bd..eff46d40 100644 --- a/changelog/632.feature.rst +++ b/changelog/632.feature.rst @@ -1 +1 @@ -``--dist=loadscope`` now sorts scopes by number of tests to assign largets scopes early -- in many cases this should improve overall test session running time, as there is less chance of a large scope being left to be processed near the end of the session, leaving other workers idle. +``--dist=loadscope`` now sorts scopes by number of tests to assign largest scopes early -- in many cases this should improve overall test session running time, as there is less chance of a large scope being left to be processed near the end of the session, leaving other workers idle.