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