LoadScope scheduler: Sort scopes by number of tests to assign biggest scopes first (#778)
Follow up to # 632. --------- Co-authored-by: Jörg Kohlsdorf <joerg@cornershopapp.com>
This commit is contained in:
committed by
GitHub
parent
f36ea25cb5
commit
3fe877bc6d
1
changelog/632.feature.rst
Normal file
1
changelog/632.feature.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
``--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.
|
||||||
@@ -350,11 +350,18 @@ class LoadScopeScheduling:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Determine chunks of work (scopes)
|
# Determine chunks of work (scopes)
|
||||||
|
unsorted_workqueue = OrderedDict()
|
||||||
for nodeid in self.collection:
|
for nodeid in self.collection:
|
||||||
scope = self._split_scope(nodeid)
|
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
|
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
|
# Avoid having more workers than work
|
||||||
extra_nodes = len(self.nodes) - len(self.workqueue)
|
extra_nodes = len(self.nodes) - len(self.workqueue)
|
||||||
|
|
||||||
|
|||||||
@@ -1232,6 +1232,22 @@ class TestLoadScope:
|
|||||||
"test_a.py::TestB", result.outlines
|
"test_a.py::TestB", result.outlines
|
||||||
) in ({"gw0": 10}, {"gw1": 10})
|
) 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:
|
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)."""
|
"""Fix test suite never finishing in case all workers start with a single test (#277)."""
|
||||||
test_file1 = """
|
test_file1 = """
|
||||||
|
|||||||
Reference in New Issue
Block a user