1from time import time
2
3
4class Timer:
5    def __init__(self, timer_name):
6        """
7        Creates timer. Timer name is name that will be in logs.
8        """
9        self.timer_name = timer_name
10        self.start_time = None
11        self.end_time = None
12        self.section_name = None
13        self.timers = []
14
15    def stop(self, section_name=''):
16        """
17        Stop timer if running. Specify section_name if want to override its name.
18        """
19        if self.start_time:
20            self.end_time = time()
21            section_name = section_name or self.section_name
22            self.timers.append((section_name, (self.end_time - self.start_time) * 1000.0))
23        self.start_time = None
24        self.section_name = None
25
26    def start(self, section_name):
27        """
28        Stop prev timer if present and starts new.
29        """
30        self.stop()
31        self.section_name = section_name
32        self.start_time = time()
33
34    def _print_timer_table(self, time_table):
35        """
36        Print a header and a footer and fill it with the timing results from time_table.
37        """
38        # TODO: Convert to use table code from here /python/common/print_utils.py#L116
39
40        if not time_table:
41            print("NOT Time Table")
42            return
43        max_header = max(len(x[0]) for x in time_table)
44        line_max_size = max_header + 14
45        print()
46        print('Timing for %s:' % self.timer_name)
47        print('=' * line_max_size)
48
49        for name, val in time_table:
50            print("%-*s %8d msec" % (max_header, name, val))
51
52        print('-' * line_max_size)
53        print(("Total: %8d msec" % sum(x[1] for x in time_table)).rjust(line_max_size))
54
55    def print_flat(self):
56        """
57        Output result.
58        """
59        self._print_timer_table(self.timers)
60
61    def print_aggregate(self):
62        """
63        Print aggregated results for each section.
64        """
65        accumulated_times = {}
66        ordered_names = []
67        for name, val in self.timers:
68            if name not in accumulated_times:
69                ordered_names.append(name)
70                accumulated_times[name] = 0
71            accumulated_times[name] += val
72        time_table = [(name, accumulated_times[name]) for name in ordered_names]
73
74        self._print_timer_table(time_table)
75
76    def print_statistics(self):
77        """
78        Print number of times, average, std, and max statistics for each section.
79        """
80        # TODO: If conversion to python 3 happens, use statistics.pstdev() to compute statistics.
81        number_samples = {}
82        means = {}
83        stds = {}
84        maxes = {}
85        ordered_names = []
86        for name, val in self.timers:
87            if name not in ordered_names:
88                ordered_names.append(name)
89                means[name] = 0
90                stds[name] = 0
91                number_samples[name] = 0
92                maxes[name] = val
93            means[name] += val
94            number_samples[name] += 1
95            maxes[name] = max(val, maxes[name])
96        for name in ordered_names:
97            means[name] = means[name] / number_samples[name]
98        for name, val in self.timers:
99            stds[name] += (val - means[name]) ** 2.0
100        for name in ordered_names:
101            stds[name] = (stds[name] / number_samples[name]) ** 0.5
102
103        max_header = max(len(x) for x in ordered_names)
104        line_max_size = max_header + 70
105        print()
106        print('Timing statistics for %s:' % self.timer_name)
107        print('=' * line_max_size)
108
109        for name in ordered_names:
110            print(("{:<{name_width}} num: {:>6d}, mean: {:>6.2f} msec, std: {:>6.2f} msec, max: {:>6.2f} msec").
111                  format(name, number_samples[name], means[name], stds[name], maxes[name], name_width=max_header))
112
113        print('=' * line_max_size)
114
115    def stop_and_print(self):
116        """
117        Stop timer, output flat result.
118        """
119        self.stop()
120        self.print_flat()
121
122    def clear_data(self):
123        """
124        Clear timer data.
125        """
126        self.timers = []
127