Source code for decent_bench.utils.progress_bar

from dataclasses import dataclass
from multiprocessing.managers import SyncManager
from queue import Queue
from threading import Thread

from rich.progress import BarColumn, Progress, TaskID, TaskProgressColumn, TextColumn, TimeRemainingColumn

from decent_bench.distributed_algorithms import DstAlgorithm


@dataclass(eq=False)
class _ProgressRecord:
    progress_bar_id: TaskID
    increment: int


[docs] class ProgressBarController: """ Controller of progress bars showing how far each algorithm has progressed and the estimated time remaining. Args: manager: used to create a progress increment queue that can be shared across processes algorithms: algorithms that will be run, each gets its own bar n_trials: number of trials the algorithms will run """ def __init__(self, manager: SyncManager, algorithms: list[DstAlgorithm], n_trials: int): self._progress_increment_queue: Queue[_ProgressRecord] = manager.Queue() orchestrator = Progress( TextColumn("{task.description}"), BarColumn(finished_style="bold green", pulse_style="none"), TaskProgressColumn(), TimeRemainingColumn(elapsed_when_finished=True), speed_estimate_period=300, ) self._progress_bar_ids = {alg: orchestrator.add_task(alg.name, total=n_trials) for alg in algorithms} orchestrator.start() listener_thread = Thread(target=self._progress_listener, args=(orchestrator, self._progress_increment_queue)) listener_thread.start()
[docs] def start_progress_bar(self, algorithm: DstAlgorithm) -> None: """ Start the clock of *algorithm*'s progress bar without incrementing it. Internally, this is done through sending an increment of 0 to the progress listener. The progress listener recognizes that the algorithm's execution just started and resets its clock, which started when the progress bar was first rendered. """ progress_bar_id = self._progress_bar_ids[algorithm] self._progress_increment_queue.put(_ProgressRecord(progress_bar_id, 0))
[docs] def advance_progress_bar(self, algorithm: DstAlgorithm) -> None: """Advance *algorithm*'s progress bar by one trial.""" progress_bar_id = self._progress_bar_ids[algorithm] self._progress_increment_queue.put(_ProgressRecord(progress_bar_id, 1))
@staticmethod def _progress_listener(orchestrator: Progress, queue: Queue[_ProgressRecord]) -> None: started_progress_bar_ids = set() while not orchestrator.finished: progress_record = queue.get() if progress_record.progress_bar_id not in started_progress_bar_ids: orchestrator.reset(progress_record.progress_bar_id) started_progress_bar_ids.add(progress_record.progress_bar_id) orchestrator.advance(progress_record.progress_bar_id, progress_record.increment) orchestrator.stop()