From: Simon Glass <sjg@chromium.org> When many boards are in flight on a high-thread machine, each make gets only -j1 (nthreads / active_boards = 256/256), causing poor efficiency when the machine is mostly idle. Track the number of in-flight boards with a locked counter in BuilderThread.run(). When Builder.num_jobs is None (meaning dynamic mode), calculate -j as nthreads / active_boards. When the in-flight count drops below the baseline (max_boards or nthreads), ramp it up by a factor of two to use more of the available CPU power. Rename _num_threads and _active_lock to public since BuilderThread accesses them from a separate module. Signed-off-by: Simon Glass <sjg@chromium.org> --- tools/buildman/builder.py | 16 ++++++++-------- tools/buildman/builderthread.py | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/tools/buildman/builder.py b/tools/buildman/builder.py index de895d43cfe..c4ec22dbc77 100644 --- a/tools/buildman/builder.py +++ b/tools/buildman/builder.py @@ -199,7 +199,7 @@ class Builder: _complete_delay: Expected delay until completion (timedelta) _next_delay_update: Next time we plan to display a progress update (datatime) - _num_threads: Number of builder threads to run + num_threads: Number of builder threads to run _opts: DisplayOptions for result output _re_make_err: Compiled regex for make error detection _restarting_config: True if 'Restart config' is detected in output @@ -304,11 +304,11 @@ class Builder: self.do_make = make_func or self.make self.gnu_make = gnu_make self.checkout = checkout - self._num_threads = num_threads + self.num_threads = num_threads self.num_jobs = num_jobs self.active_boards = 0 self.max_boards = 0 - self._active_lock = threading.Lock() + self.active_lock = threading.Lock() self._already_done = 0 self.kconfig_reconfig = 0 self.force_build = False @@ -403,11 +403,11 @@ class Builder: test_thread_exceptions (bool): True to make threads raise an exception instead of reporting their result (for tests) """ - if self._num_threads: + if self.num_threads: self._single_builder = None self.queue = queue.Queue() self.out_queue = queue.Queue() - for i in range(self._num_threads): + for i in range(self.num_threads): t = self._thread_class( self, i, mrproper, per_board_out_dir, test_exception=test_thread_exceptions) @@ -1227,7 +1227,7 @@ class Builder: self._result_handler.reset_result_summary(board_selected) builderthread.mkdir(self.base_dir, parents = True) - self._prepare_working_space(min(self._num_threads, len(board_selected)), + self._prepare_working_space(min(self.num_threads, len(board_selected)), board_selected and commits is not None) self._prepare_output_space() if not self._opts.ide: @@ -1247,7 +1247,7 @@ class Builder: job.adjust_cfg = self.adjust_cfg job.fragments = fragments job.step = self._step - if self._num_threads: + if self.num_threads: self.queue.put(job) else: self._single_builder.run_job(job) @@ -1268,7 +1268,7 @@ class Builder: - number of boards that issued warnings - list of thread exceptions raised """ - if self._num_threads: + if self.num_threads: term = threading.Thread(target=self.queue.join) term.daemon = True term.start() diff --git a/tools/buildman/builderthread.py b/tools/buildman/builderthread.py index 13c98612c81..16534196d4d 100644 --- a/tools/buildman/builderthread.py +++ b/tools/buildman/builderthread.py @@ -332,8 +332,18 @@ class BuilderThread(threading.Thread): args.append('V=1') else: args.append('-s') - if self.builder.num_jobs is not None: - args.extend(['-j', str(self.builder.num_jobs)]) + num_jobs = self.builder.num_jobs + if num_jobs is None and self.builder.active_boards: + active = self.builder.active_boards + nthreads = self.builder.num_threads + baseline = self.builder.max_boards or nthreads + if active < baseline: + # Tail: ramp up 2x to compensate for make overhead + num_jobs = max(1, nthreads * 2 // active) + else: + num_jobs = max(1, nthreads // active) + if num_jobs is not None: + args.extend(['-j', str(num_jobs)]) if self.builder.warnings_as_errors: args.append('KCFLAGS=-Werror') args.append('HOSTCFLAGS=-Werror') @@ -1022,10 +1032,15 @@ class BuilderThread(threading.Thread): """ while True: job = self.builder.queue.get() + with self.builder.active_lock: + self.builder.active_boards += 1 try: self.run_job(job) except Exception as exc: # pylint: disable=W0718 print('Thread exception (use -T0 to run without threads):', exc) self.builder.thread_exceptions.append(exc) + finally: + with self.builder.active_lock: + self.builder.active_boards -= 1 self.builder.queue.task_done() -- 2.43.0