Skip to content

Commit c03c104

Browse files
authored
Merge pull request SCons#4469 from acmorrow/newsched-dynamic-workers
NewParallel only adds worker threads as executable tasks are discovered
2 parents ef925ad + dca250f commit c03c104

File tree

5 files changed

+72
-49
lines changed

5 files changed

+72
-49
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
6868
- CacheDir writes no longer happen within the taskmaster critical section,
6969
and therefore can run in parallel with both other CacheDir writes and the
7070
taskmaster DAG walk.
71+
- The NewParallel scheduler now only adds threads as new work requiring execution
72+
is discovered, up to the limit set by -j. This should reduce resource utilization
73+
when the achievable parallelism in the DAG is less than the -j limit.
7174

7275
From Mats Wichmann:
7376
- Add support for Python 3.13 (as of alpha 2). So far only affects

RELEASE.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ IMPROVEMENTS
6767
(Larger -j values)
6868
- CacheDir writes no longer happen within the taskmaster critical section, and therefore
6969
can run in parallel with both other CacheDir writes and the taskmaster DAG walk.
70+
- The NewParallel scheduler now only adds threads as new work requiring execution
71+
is discovered, up to the limit set by -j. This should reduce resource utilization
72+
when the achievable parallelism in the DAG is less than the -j limit.
7073

7174

7275
PACKAGING

SCons/Taskmaster/Job.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ def __exit__(self, *args):
474474

475475
def __init__(self, taskmaster, num, stack_size) -> None:
476476
self.taskmaster = taskmaster
477-
self.num_workers = num
477+
self.max_workers = num
478478
self.stack_size = stack_size
479479
self.interrupted = InterruptState()
480480
self.workers = []
@@ -484,7 +484,7 @@ def __init__(self, taskmaster, num, stack_size) -> None:
484484
# also protects access to our state that gets updated
485485
# concurrently. The `can_search_cv` is associated with
486486
# this mutex.
487-
self.tm_lock = (threading.Lock if self.num_workers > 1 else NewParallel.FakeLock)()
487+
self.tm_lock = (threading.Lock if self.max_workers > 1 else NewParallel.FakeLock)()
488488

489489
# Guarded under `tm_lock`.
490490
self.jobs = 0
@@ -493,11 +493,11 @@ def __init__(self, taskmaster, num, stack_size) -> None:
493493
# The `can_search_cv` is used to manage a leader /
494494
# follower pattern for access to the taskmaster, and to
495495
# awaken from stalls.
496-
self.can_search_cv = (threading.Condition if self.num_workers > 1 else NewParallel.FakeCondition)(self.tm_lock)
496+
self.can_search_cv = (threading.Condition if self.max_workers > 1 else NewParallel.FakeCondition)(self.tm_lock)
497497

498498
# The queue of tasks that have completed execution. The
499499
# next thread to obtain `tm_lock`` will retire them.
500-
self.results_queue_lock = (threading.Lock if self.num_workers > 1 else NewParallel.FakeLock)()
500+
self.results_queue_lock = (threading.Lock if self.max_workers > 1 else NewParallel.FakeLock)()
501501
self.results_queue = []
502502

503503
if self.taskmaster.trace:
@@ -516,22 +516,27 @@ def trace_message(self, message) -> None:
516516
method_name = sys._getframe(1).f_code.co_name + "():"
517517
thread_id=threading.get_ident()
518518
self.trace.debug('%s.%s [Thread:%s] %s' % (type(self).__name__, method_name, thread_id, message))
519-
# print('%-15s %s' % (method_name, message))
520519

521520
def start(self) -> None:
522-
if self.num_workers == 1:
521+
if self.max_workers == 1:
523522
self._work()
524523
else:
525-
self._start_workers()
526-
for worker in self.workers:
527-
worker.join()
528-
self.workers = []
524+
self._start_worker()
525+
while len(self.workers) > 0:
526+
self.workers[0].join()
527+
self.workers.pop(0)
529528
self.taskmaster.cleanup()
530529

531-
def _start_workers(self) -> None:
530+
def _maybe_start_worker(self) -> None:
531+
if self.max_workers > 1 and len(self.workers) < self.max_workers:
532+
if self.jobs >= len(self.workers):
533+
self._start_worker()
534+
535+
def _start_worker(self) -> None:
532536
prev_size = self._adjust_stack_size()
533-
for _ in range(self.num_workers):
534-
self.workers.append(NewParallel.Worker(self))
537+
if self.trace:
538+
self.trace_message("Starting new worker thread")
539+
self.workers.append(NewParallel.Worker(self))
535540
self._restore_stack_size(prev_size)
536541

537542
def _adjust_stack_size(self):
@@ -680,6 +685,11 @@ def _work(self):
680685
self.trace_message("Found task requiring execution")
681686
self.state = NewParallel.State.READY
682687
self.can_search_cv.notify()
688+
# This thread will be busy taking care of
689+
# `execute`ing this task. If we haven't
690+
# reached the limit, spawn a new thread to
691+
# turn the crank and find the next task.
692+
self._maybe_start_worker()
683693

684694
else:
685695
# We failed to find a task, so this thread

test/option/fixture/taskmaster_expected_new_parallel.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
Job.NewParallel._start_worker(): [Thread:XXXXX] Starting new worker thread
12
Job.NewParallel._work(): [Thread:XXXXX] Gained exclusive access
23
Job.NewParallel._work(): [Thread:XXXXX] Starting search
34
Job.NewParallel._work(): [Thread:XXXXX] Found 0 completed tasks to process
@@ -86,5 +87,3 @@ Taskmaster: No candidate anymore.
8687
Job.NewParallel._work(): [Thread:XXXXX] Found no task requiring execution, and have no jobs: marking complete
8788
Job.NewParallel._work(): [Thread:XXXXX] Gained exclusive access
8889
Job.NewParallel._work(): [Thread:XXXXX] Completion detected, breaking from main loop
89-
Job.NewParallel._work(): [Thread:XXXXX] Gained exclusive access
90-
Job.NewParallel._work(): [Thread:XXXXX] Completion detected, breaking from main loop

0 commit comments

Comments
 (0)