Skip to content

Commit df71225

Browse files
committed
Implement work-stealing scheduling
Closes #858
1 parent 26d94ce commit df71225

File tree

8 files changed

+533
-2
lines changed

8 files changed

+533
-2
lines changed

changelog/858.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
New ``worksteal`` scheduler, based on the idea of `work stealing <https://en.wikipedia.org/wiki/Work_stealing>`_. It's similar to ``load`` scheduler, but it should handle tests with significantly differing duration better, and, at the same time, it should provide similar or better reuse of fixtures.

docs/distribution.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,13 @@ The test distribution algorithm is configured with the ``--dist`` command-line o
8282
This will make sure ``test1`` and ``TestA::test2`` will run in the same worker.
8383
Tests without the ``xdist_group`` mark are distributed normally as in the ``--dist=load`` mode.
8484

85+
* ``--dist worksteal``: Initially, tests are distributed evenly among all
86+
available workers. When a worker completes most of its assigned tests and
87+
doesn't have enough tests to continue (currently, every worker needs at least
88+
two tests in its queue), an attempt is made to reassign ("steal") a portion
89+
of tests from some other worker's queue. The results should be similar to
90+
the ``load`` method, but ``worksteal`` should handle tests with significantly
91+
differing duration better, and, at the same time, it should provide similar
92+
or better reuse of fixtures.
93+
8594
* ``--dist no``: The normal pytest execution mode, runs one test at a time (no distribution at all).

src/xdist/dsession.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
LoadScopeScheduling,
99
LoadFileScheduling,
1010
LoadGroupScheduling,
11+
WorkStealingScheduling,
1112
)
1213

1314

@@ -100,6 +101,7 @@ def pytest_xdist_make_scheduler(self, config, log):
100101
"loadscope": LoadScopeScheduling,
101102
"loadfile": LoadFileScheduling,
102103
"loadgroup": LoadGroupScheduling,
104+
"worksteal": WorkStealingScheduling,
103105
}
104106
return schedulers[dist](config, log)
105107

@@ -282,6 +284,17 @@ def worker_runtest_protocol_complete(self, node, item_index, duration):
282284
"""
283285
self.sched.mark_test_complete(node, item_index, duration)
284286

287+
def worker_unscheduled(self, node, indices):
288+
"""
289+
Emitted when a node fires the 'unscheduled' event, signalling that
290+
some tests have been removed from the worker's queue and should be
291+
sent to some worker again.
292+
293+
This should happen only in response to 'steal' command, so schedulers
294+
not using 'steal' command don't have to implement it.
295+
"""
296+
self.sched.remove_pending_tests_from_node(node, indices)
297+
285298
def worker_collectreport(self, node, rep):
286299
"""Emitted when a node calls the pytest_collectreport hook.
287300

src/xdist/plugin.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,15 @@ def pytest_addoption(parser):
9494
"--dist",
9595
metavar="distmode",
9696
action="store",
97-
choices=["each", "load", "loadscope", "loadfile", "loadgroup", "no"],
97+
choices=[
98+
"each",
99+
"load",
100+
"loadscope",
101+
"loadfile",
102+
"loadgroup",
103+
"worksteal",
104+
"no",
105+
],
98106
dest="dist",
99107
default="no",
100108
help=(
@@ -107,6 +115,8 @@ def pytest_addoption(parser):
107115
"loadfile: load balance by sending test grouped by file"
108116
" to any available environment.\n\n"
109117
"loadgroup: like load, but sends tests marked with 'xdist_group' to the same worker.\n\n"
118+
"worksteal: split the test suite between available environments,"
119+
" then rebalance when any worker runs out of tests.\n\n"
110120
"(default) no: run tests inprocess, don't distribute."
111121
),
112122
)

src/xdist/scheduler/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
from xdist.scheduler.loadfile import LoadFileScheduling # noqa
44
from xdist.scheduler.loadscope import LoadScopeScheduling # noqa
55
from xdist.scheduler.loadgroup import LoadGroupScheduling # noqa
6+
from xdist.scheduler.worksteal import WorkStealingScheduling # noqa

0 commit comments

Comments
 (0)