aboutsummaryrefslogtreecommitdiff
path: root/crosperf/experiment_status.py
diff options
context:
space:
mode:
Diffstat (limited to 'crosperf/experiment_status.py')
-rw-r--r--crosperf/experiment_status.py145
1 files changed, 145 insertions, 0 deletions
diff --git a/crosperf/experiment_status.py b/crosperf/experiment_status.py
new file mode 100644
index 00000000..627db99e
--- /dev/null
+++ b/crosperf/experiment_status.py
@@ -0,0 +1,145 @@
+# Copyright 2011 The Chromium OS Authors. All rights reserved.
+# 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."""
+
+from __future__ import print_function
+
+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.iteritems():
+ 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.iteritems():
+ 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.iteritems():
+ val.sort()
+ iterations = ','.join(map(str, val))
+ strings.append('{} [{}]'.format(key, iterations))
+ output_segs.append(' ' + label_name + ': ' + ', '.join(strings) + '\n')
+
+ return ' %s \n%s' % (len(benchmark_runs), ''.join(output_segs))