1 | from __future__ import print_function |
---|
2 | # Only Python 2.6 and up, because of NamedTuple. |
---|
3 | import time |
---|
4 | from collections import namedtuple |
---|
5 | Score = namedtuple('Score', ['tag', 'stamp']) |
---|
6 | |
---|
7 | |
---|
8 | class TimeCollector(object): |
---|
9 | def __init__(self): |
---|
10 | '''The first time stamp is created here''' |
---|
11 | self.scores = [Score(tag='start', stamp=time.clock())] |
---|
12 | |
---|
13 | def addStamp(self, description): |
---|
14 | '''Adds a new time stamp, with a description.''' |
---|
15 | self.scores.append(Score(tag=description, stamp=time.clock())) |
---|
16 | |
---|
17 | def _stampDelta(self, index1, index2): |
---|
18 | '''Private utility function to clean up this common calculation.''' |
---|
19 | return self.scores[index1].stamp - self.scores[index2].stamp |
---|
20 | |
---|
21 | def getReportItems(self, orderByCost=True): |
---|
22 | '''Returns a list of dicts. Each dict has |
---|
23 | start (ms), |
---|
24 | end (ms), |
---|
25 | delta (ms), |
---|
26 | perc (%), |
---|
27 | tag (str) |
---|
28 | ''' |
---|
29 | self.scores.append(Score(tag='finish', stamp=time.clock())) |
---|
30 | total_time = self._stampDelta(-1, 0) |
---|
31 | data = [] |
---|
32 | for i in range(1, len(self.scores)): |
---|
33 | delta = self._stampDelta(i, i - 1) |
---|
34 | if abs(total_time) < 1e-6: |
---|
35 | perc = 0 |
---|
36 | else: |
---|
37 | perc = delta / total_time * 100 |
---|
38 | data.append( |
---|
39 | dict( |
---|
40 | start=self._stampDelta(i - 1, 0) * 1000, |
---|
41 | end=self._stampDelta(i, 0) * 1000, |
---|
42 | delta=delta * 1000, |
---|
43 | perc=perc, |
---|
44 | tag=self.scores[i].tag |
---|
45 | ) |
---|
46 | ) |
---|
47 | if orderByCost: |
---|
48 | data.sort(key=lambda x: x['perc'], reverse=True) |
---|
49 | return data |
---|
50 | |
---|
51 | def getReportLines(self, orderByCost=True): |
---|
52 | '''Produces a report of logged time-stamps as a list of strings. |
---|
53 | if orderByCost is False, then the order of the stamps is |
---|
54 | chronological.''' |
---|
55 | data = self.getReportItems(orderByCost) |
---|
56 | headerTemplate = '%10s | %10s | %10s | %11s | %-30s' |
---|
57 | headerData = ('Start(ms)', 'End(ms)', 'Delta(ms)', 'Time Cost', |
---|
58 | 'Description') |
---|
59 | bodyTemplate = '%(start)10.0f | %(end)10.0f | %(delta)10.0f |' \ |
---|
60 | + ' %(perc)10.0f%% | %(tag)-30s' |
---|
61 | return [headerTemplate % headerData] + [bodyTemplate % d for d in data] |
---|
62 | |
---|
63 | def getReportText(self, **kwargs): |
---|
64 | return '\n'.join(self.getReportLines(**kwargs)) |
---|
65 | |
---|
66 | def restart(self): |
---|
67 | self.scores = [Score(tag='start', stamp=time.clock())] |
---|
68 | |
---|
69 | if __name__ == '__main__': |
---|
70 | print('') |
---|
71 | print('Testing:') |
---|
72 | print('') |
---|
73 | |
---|
74 | # First create the collector |
---|
75 | t = TimeCollector() |
---|
76 | x = [i for i in range(1000)] |
---|
77 | # Every time some work gets done, add a stamp |
---|
78 | t.addStamp('Initialization Section') |
---|
79 | x = [i for i in range(10000)] |
---|
80 | t.addStamp('A big loop') |
---|
81 | x = [i for i in range(100000)] |
---|
82 | t.addStamp('calling builder function') |
---|
83 | # Finally, obtain the results |
---|
84 | print('') |
---|
85 | print(t.getReportText()) |
---|
86 | # If you want to measure something else in the same scope, you can |
---|
87 | # restart the collector. |
---|
88 | t.restart() |
---|
89 | x = [i for i in range(1000000)] |
---|
90 | t.addStamp('Part 2') |
---|
91 | x = [i for i in range(1000000)] |
---|
92 | t.addStamp('Cleanup') |
---|
93 | # And once again report results |
---|
94 | print('') |
---|
95 | print(t.getReportText()) |
---|
96 | t.restart() |
---|
97 | for y in range(1, 200, 20): |
---|
98 | x = [i for i in range(10000) * y] |
---|
99 | t.addStamp('Iteration when y = ' + str(y)) |
---|
100 | |
---|
101 | print('') |
---|
102 | # You can turn off ordering of results |
---|
103 | print(t.getReportText(orderByCost=False)) |
---|