experiment_status.py revision 2317decf0e3f62297fc09a712bdf7fa253d560f3
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
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, num_undone_chars *
28                        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)),
80                    eta))
81    strings.append(self._GetProgressBar(self.experiment.num_complete,
82                                        self.num_total))
83    return "\n".join(strings)
84
85  def GetStatusString(self):
86    """Get the status string of all the benchmark_runs."""
87    status_bins = {}
88    for benchmark_run in self.experiment.benchmark_runs:
89      if benchmark_run.timeline.GetLastEvent() not in status_bins:
90        status_bins[benchmark_run.timeline.GetLastEvent()] = []
91      status_bins[benchmark_run.timeline.GetLastEvent()].append(benchmark_run)
92
93    status_strings = []
94    for key, val in status_bins.items():
95      status_strings.append("%s: %s" %
96                            (key, self._GetNamesAndIterations(val)))
97    result = "Thread Status:\n%s" % "\n".join(status_strings)
98
99    # Add the machine manager status.
100    result += "\n" + self.experiment.machine_manager.AsString() + "\n"
101
102    return result
103
104  def _GetNamesAndIterations(self, benchmark_runs):
105    strings = []
106    t = time.time()
107    for benchmark_run in benchmark_runs:
108      t_last = benchmark_run.timeline.GetLastEventTime()
109      elapsed = str(datetime.timedelta(seconds=int(t-t_last)))
110      strings.append("'{0}' {1}".format(benchmark_run.name, elapsed))
111    return " %s (%s)" % (len(strings), ", ".join(strings))
112