| # -*- coding: utf-8 -*- |
| # Copyright 2011 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """The class to show the banner.""" |
| |
| |
| import collections |
| import datetime |
| import time |
| |
| |
| class ExperimentStatus(object): |
| """The status class.""" |
| |
| def __init__(self, experiment): |
| self.experiment = experiment |
| self.num_total = len(self.experiment.benchmark_runs) |
| self.completed = 0 |
| self.new_job_start_time = time.time() |
| self.log_level = experiment.log_level |
| |
| def _GetProgressBar(self, num_complete, num_total): |
| ret = "Done: %s%%" % int(100.0 * num_complete / num_total) |
| bar_length = 50 |
| done_char = ">" |
| undone_char = " " |
| num_complete_chars = bar_length * num_complete // num_total |
| num_undone_chars = bar_length - num_complete_chars |
| ret += " [%s%s]" % ( |
| num_complete_chars * done_char, |
| num_undone_chars * undone_char, |
| ) |
| return ret |
| |
| def GetProgressString(self): |
| """Get the elapsed_time, ETA.""" |
| current_time = time.time() |
| if self.experiment.start_time: |
| elapsed_time = current_time - self.experiment.start_time |
| else: |
| elapsed_time = 0 |
| try: |
| if self.completed != self.experiment.num_complete: |
| self.completed = self.experiment.num_complete |
| self.new_job_start_time = current_time |
| time_completed_jobs = elapsed_time - ( |
| current_time - self.new_job_start_time |
| ) |
| # eta is calculated as: |
| # ETA = (num_jobs_not_yet_started * estimated_time_per_job) |
| # + time_left_for_current_job |
| # |
| # where |
| # num_jobs_not_yet_started = (num_total - num_complete - 1) |
| # |
| # estimated_time_per_job = time_completed_jobs / num_run_complete |
| # |
| # time_left_for_current_job = estimated_time_per_job - |
| # time_spent_so_far_on_current_job |
| # |
| # The biggest problem with this calculation is its assumption that |
| # all jobs have roughly the same running time (blatantly false!). |
| # |
| # ETA can come out negative if the time spent on the current job is |
| # greater than the estimated time per job (e.g. you're running the |
| # first long job, after a series of short jobs). For now, if that |
| # happens, we set the ETA to "Unknown." |
| # |
| eta_seconds = float( |
| self.num_total - self.experiment.num_complete - 1 |
| ) * time_completed_jobs / self.experiment.num_run_complete + ( |
| time_completed_jobs / self.experiment.num_run_complete |
| - (current_time - self.new_job_start_time) |
| ) |
| |
| eta_seconds = int(eta_seconds) |
| if eta_seconds > 0: |
| eta = datetime.timedelta(seconds=eta_seconds) |
| else: |
| eta = "Unknown" |
| except ZeroDivisionError: |
| eta = "Unknown" |
| strings = [] |
| strings.append( |
| "Current time: %s Elapsed: %s ETA: %s" |
| % ( |
| datetime.datetime.now(), |
| datetime.timedelta(seconds=int(elapsed_time)), |
| eta, |
| ) |
| ) |
| strings.append( |
| self._GetProgressBar(self.experiment.num_complete, self.num_total) |
| ) |
| return "\n".join(strings) |
| |
| def GetStatusString(self): |
| """Get the status string of all the benchmark_runs.""" |
| status_bins = collections.defaultdict(list) |
| for benchmark_run in self.experiment.benchmark_runs: |
| status_bins[benchmark_run.timeline.GetLastEvent()].append( |
| benchmark_run |
| ) |
| |
| status_strings = [] |
| for key, val in status_bins.items(): |
| if key == "RUNNING": |
| get_description = self._GetNamesAndIterations |
| else: |
| get_description = self._GetCompactNamesAndIterations |
| status_strings.append("%s: %s" % (key, get_description(val))) |
| |
| thread_status = "" |
| thread_status_format = "Thread Status: \n{}\n" |
| if ( |
| self.experiment.schedv2() is None |
| and self.experiment.log_level == "verbose" |
| ): |
| # Add the machine manager status. |
| thread_status = thread_status_format.format( |
| self.experiment.machine_manager.AsString() |
| ) |
| elif self.experiment.schedv2(): |
| # In schedv2 mode, we always print out thread status. |
| thread_status = thread_status_format.format( |
| self.experiment.schedv2().threads_status_as_string() |
| ) |
| |
| result = "{}{}".format(thread_status, "\n".join(status_strings)) |
| |
| return result |
| |
| def _GetNamesAndIterations(self, benchmark_runs): |
| strings = [] |
| t = time.time() |
| for benchmark_run in benchmark_runs: |
| t_last = benchmark_run.timeline.GetLastEventTime() |
| elapsed = str(datetime.timedelta(seconds=int(t - t_last))) |
| strings.append("'{0}' {1}".format(benchmark_run.name, elapsed)) |
| return " %s (%s)" % (len(strings), ", ".join(strings)) |
| |
| def _GetCompactNamesAndIterations(self, benchmark_runs): |
| grouped_benchmarks = collections.defaultdict(list) |
| for benchmark_run in benchmark_runs: |
| grouped_benchmarks[benchmark_run.label.name].append(benchmark_run) |
| |
| output_segs = [] |
| for label_name, label_runs in grouped_benchmarks.items(): |
| strings = [] |
| benchmark_iterations = collections.defaultdict(list) |
| for benchmark_run in label_runs: |
| assert benchmark_run.label.name == label_name |
| benchmark_name = benchmark_run.benchmark.name |
| benchmark_iterations[benchmark_name].append( |
| benchmark_run.iteration |
| ) |
| for key, val in benchmark_iterations.items(): |
| val.sort() |
| iterations = ",".join(str(v) for v in val) |
| strings.append("{} [{}]".format(key, iterations)) |
| output_segs.append( |
| " " + label_name + ": " + ", ".join(strings) + "\n" |
| ) |
| |
| return " %s \n%s" % (len(benchmark_runs), "".join(output_segs)) |