experiment_status.py revision f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbe
1# Copyright 2011 Google Inc. All Rights Reserved.
2"""The class to show the banner."""
3
4from __future__ import print_function
5
6import datetime
7import time
8
9
10class ExperimentStatus(object):
11  """The status class."""
12
13  def __init__(self, experiment):
14    self.experiment = experiment
15    self.num_total = len(self.experiment.benchmark_runs)
16    self.completed = 0
17    self.new_job_start_time = time.time()
18    self.log_level = experiment.log_level
19
20  def _GetProgressBar(self, num_complete, num_total):
21    ret = 'Done: %s%%' % int(100.0 * num_complete / num_total)
22    bar_length = 50
23    done_char = '>'
24    undone_char = ' '
25    num_complete_chars = bar_length * num_complete / num_total
26    num_undone_chars = bar_length - num_complete_chars
27    ret += ' [%s%s]' % (num_complete_chars * done_char,
28                        num_undone_chars * undone_char)
29    return ret
30
31  def GetProgressString(self):
32    """Get the elapsed_time, ETA."""
33    current_time = time.time()
34    if self.experiment.start_time:
35      elapsed_time = current_time - self.experiment.start_time
36    else:
37      elapsed_time = 0
38    try:
39      if self.completed != self.experiment.num_complete:
40        self.completed = self.experiment.num_complete
41        self.new_job_start_time = current_time
42      time_completed_jobs = (elapsed_time -
43                             (current_time - self.new_job_start_time))
44      # eta is calculated as:
45      #   ETA = (num_jobs_not_yet_started * estimated_time_per_job)
46      #          + time_left_for_current_job
47      #
48      #   where
49      #        num_jobs_not_yet_started = (num_total - num_complete - 1)
50      #
51      #        estimated_time_per_job = time_completed_jobs / num_run_complete
52      #
53      #        time_left_for_current_job = estimated_time_per_job -
54      #                                    time_spent_so_far_on_current_job
55      #
56      #  The biggest problem with this calculation is its assumption that
57      #  all jobs have roughly the same running time (blatantly false!).
58      #
59      #  ETA can come out negative if the time spent on the current job is
60      #  greater than the estimated time per job (e.g. you're running the
61      #  first long job, after a series of short jobs).  For now, if that
62      #  happens, we set the ETA to "Unknown."
63      #
64      eta_seconds = (float(self.num_total - self.experiment.num_complete - 1) *
65                     time_completed_jobs / self.experiment.num_run_complete +
66                     (time_completed_jobs / self.experiment.num_run_complete -
67                      (current_time - self.new_job_start_time)))
68
69      eta_seconds = int(eta_seconds)
70      if eta_seconds > 0:
71        eta = datetime.timedelta(seconds=eta_seconds)
72      else:
73        eta = 'Unknown'
74    except ZeroDivisionError:
75      eta = 'Unknown'
76    strings = []
77    strings.append('Current time: %s Elapsed: %s ETA: %s' %
78                   (datetime.datetime.now(),
79                    datetime.timedelta(seconds=int(elapsed_time)), eta))
80    strings.append(self._GetProgressBar(self.experiment.num_complete,
81                                        self.num_total))
82    return '\n'.join(strings)
83
84  def GetStatusString(self):
85    """Get the status string of all the benchmark_runs."""
86    status_bins = {}
87    for benchmark_run in self.experiment.benchmark_runs:
88      if benchmark_run.timeline.GetLastEvent() not in status_bins:
89        status_bins[benchmark_run.timeline.GetLastEvent()] = []
90      status_bins[benchmark_run.timeline.GetLastEvent()].append(benchmark_run)
91
92    status_strings = []
93    for key, val in status_bins.items():
94      if key == 'RUNNING':
95        status_strings.append('%s: %s' %
96                              (key, self._GetNamesAndIterations(val)))
97      else:
98        status_strings.append('%s: %s' %
99                              (key, self._GetCompactNamesAndIterations(val)))
100
101    thread_status = ''
102    thread_status_format = 'Thread Status: \n{}\n'
103    if (self.experiment.schedv2() is None and
104        self.experiment.log_level == 'verbose'):
105      # Add the machine manager status.
106      thread_status = thread_status_format.format(
107          self.experiment.machine_manager.AsString())
108    elif self.experiment.schedv2():
109      # In schedv2 mode, we always print out thread status.
110      thread_status = thread_status_format.format(self.experiment.schedv2(
111      ).threads_status_as_string())
112
113    result = '{}{}'.format(thread_status, '\n'.join(status_strings))
114
115    return result
116
117  def _GetNamesAndIterations(self, benchmark_runs):
118    strings = []
119    t = time.time()
120    for benchmark_run in benchmark_runs:
121      t_last = benchmark_run.timeline.GetLastEventTime()
122      elapsed = str(datetime.timedelta(seconds=int(t - t_last)))
123      strings.append("'{0}' {1}".format(benchmark_run.name, elapsed))
124    return ' %s (%s)' % (len(strings), ', '.join(strings))
125
126  def _GetCompactNamesAndIterations(self, benchmark_runs):
127    output = ''
128    labels = {}
129    for benchmark_run in benchmark_runs:
130      if benchmark_run.label.name not in labels:
131        labels[benchmark_run.label.name] = []
132
133    for label in labels:
134      strings = []
135      benchmark_iterations = {}
136      for benchmark_run in benchmark_runs:
137        if benchmark_run.label.name != label:
138          continue
139        benchmark_name = benchmark_run.benchmark.name
140        if benchmark_name not in benchmark_iterations:
141          benchmark_iterations[benchmark_name] = []
142        benchmark_iterations[benchmark_name].append(benchmark_run.iteration)
143      for key, val in benchmark_iterations.items():
144        val.sort()
145        iterations = ','.join(map(str, val))
146        strings.append('{} [{}]'.format(key, iterations))
147      output += '  ' + label + ': ' + ', '.join(strings) + '\n'
148
149    return ' %s \n%s' % (len(benchmark_runs), output)
150