experiment_status.py revision 3c43b87571a5f46b73c62ed92f11b5868e9f38c5
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      status_strings.append("%s: %s" %
97                            (key, self._GetNamesAndIterations(val)))
98
99    thread_status = ""
100    thread_status_format = "Thread Status: \n{}\n"
101    if (self.experiment.schedv2() is None and
102        self.experiment.log_level == "verbose"):
103        # Add the machine manager status.
104        thread_status = thread_status_format.format(
105          self.experiment.machine_manager.AsString())
106    elif self.experiment.schedv2():
107        # In schedv2 mode, we always print out thread status.
108        thread_status = thread_status_format.format(
109            self.experiment.schedv2().threads_status_as_string())
110
111    result = "{}{}".format(thread_status, "\n".join(status_strings))
112
113    return result
114
115  def _GetNamesAndIterations(self, benchmark_runs):
116    strings = []
117    t = time.time()
118    for benchmark_run in benchmark_runs:
119      t_last = benchmark_run.timeline.GetLastEventTime()
120      elapsed = str(datetime.timedelta(seconds=int(t-t_last)))
121      if self.experiment.log_level == "verbose":
122        strings.append("'{0}' {1}".format(benchmark_run.name, elapsed))
123      else:
124        strings.append("'{0}'".format(benchmark_run.name))
125    return " %s (%s)" % (len(strings), ", ".join(strings))
126