-
Notifications
You must be signed in to change notification settings - Fork 1
/
benchmark.py
210 lines (167 loc) · 7.22 KB
/
benchmark.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# -*- coding: utf-8 -*-
from timeit import default_timer as timer
###
# This file will usually not need any alteration.
###
#
# Class to wrap a benchmark (helper functions)
class Benchmark(object):
def __init__(self):
self.totalTime = {}
self.iterations = {}
self.startTime = {}
self.depth = 0
self.events = []
# True will make all benchmark instances log their actions in an easy to follow manner
# Might however use up more memory as each action is logged - defaults to False. Use with benchmark.printEvents()
self.verbose = False
def __str__(self):
totalTimePrint = ', '.join('"%s": %.6f'%(key, value) for key, value in self.totalTime.items())
iterationsPrint = ', '.join('"%s": %d'%(key, value) for key, value in self.iterations.items())
return "Benchmark <Total time: {%s}, iterations: {%s}>"%(totalTimePrint, iterationsPrint)
def __repr__(self):
totalTimePrint = ', '.join('"%s": %.6f'%(key, value) for key, value in self.totalTime.items())
iterationsPrint = ', '.join('"%s": %d'%(key, value) for key, value in self.iterations.items())
return "Benchmark <Total time: {%s}, iterations: {%s}>"%(totalTimePrint, iterationsPrint)
def __add__(self, other):
self.mergeDictionaries(self.totalTime, other.totalTime)
self.mergeDictionaries(self.iterations, other.iterations)
self.mergeDictionaries(self.startTime, other.startTime)
return self
def __radd__(self, other):
return self.__add__(other)
def asTables(self):
timeLatex = """$\\begin{array}{l | l}
\\text{name} & \\text{time }s\\\\
\\hline\n """
iterationsLatex = """$\\begin{array}{l | l}
\\text{name} & \\text{iterations}\\\\
\\hline\n """
for key, value in self.totalTime.items():
timeLatex += "\\text{%s}&%f\\\\\n"%(key, value)
for key, value in self.iterations.items():
iterationsLatex += "\\text{%s}&%d\\\\\n"%(key, value)
timeLatex += "\\end{array}$"
iterationsLatex += "\\end{array}$"
return (timeLatex, iterationsLatex)
def mergeDictionaries(self, a, b):
for key in b.keys():
if key not in a:
a[key] = b[key]
else:
a[key] += b[key]
def start(self, name = "default"):
t = timer()
if ":" in name or "," in name or "-" in name:
raise ValueError("A benchmark name may not contain characters ':', '-' or ','")
if name in self.startTime:
raise ValueError("Benchmark for %s is already in progress, please stop benchmark first"%name)
self.startTime[name] = t
if self.verbose:
self.events.append(("start", name, self.depth, None))
self.depth += 1
def stop(self, name = "default"):
t = timer()
if ":" in name or "," in name or "-" in name:
raise ValueError("A benchmark name may not contain characters ':', '-' or ','")
if name not in self.startTime:
raise ValueError("Benchmark for %s cannot be stopped before it has been started"%name)
if name not in self.totalTime:
self.totalTime[name] = 0
timeTaken = t - self.startTime[name]
self.totalTime[name] += timeTaken
del self.startTime[name]
if self.verbose:
self.depth -= 1
self.events.append(("stop", name, self.depth, timeTaken))
def iterate(self, name = "default"):
if ":" in name or "," in name or "-" in name:
raise ValueError("A benchmark name may not contain characters ':', '-' or ','")
if name not in self.iterations:
self.iterations[name] = 0
self.iterations[name] += 1
if self.verbose:
self.events.append(("iterate", name, self.depth, None))
def printEvents(self):
if self.verbose == False or len(self.events) == 0:
print "It seems like no events were captured. Try setting verbose = True on the benchmark instance"
i = 0
while i < len(self.events):
type, name, depth, timeTaken = self.events[i]
if type == "iterate":
start = i
# Group identical iterations to preserve output length
for j in range(i + 1, len(self.events)):
otherType, otherName, otherDepth, otherTime = self.events[j]
if otherType == type and otherName == name:
i += 1
else:
break
# Print number of occurances if multiple iterations followed each other
if start == i:
print "\t" * depth + "🔂 %s"%name
else:
print "\t" * depth + "🔂 %s x %d"%(name, i - start + 1)
elif type == "start":
print "\t" * depth + "▶️ %s"%name
elif type == "stop":
print "\t" * depth + "⏹ %s (%f s)"%(name, timeTaken)
i += 1
@classmethod
def max(self, benchmarks):
benchmark = Benchmark()
totalTime = {}
iterations = {}
for b in benchmarks:
for key, value in b.totalTime.items():
if key not in totalTime:
totalTime[key] = []
totalTime[key].append(value)
for key, value in b.iterations.items():
if key not in iterations:
iterations[key] = []
iterations[key].append(value)
for key, values in totalTime.items():
benchmark.totalTime[key] = max(values)
for key, values in iterations.items():
benchmark.iterations[key] = max(values)
return benchmark
@classmethod
def min(self, benchmarks):
benchmark = Benchmark()
totalTime = {}
iterations = {}
for b in benchmarks:
for key, value in b.totalTime.items():
if key not in totalTime:
totalTime[key] = []
totalTime[key].append(value)
for key, value in b.iterations.items():
if key not in iterations:
iterations[key] = []
iterations[key].append(value)
for key, values in totalTime.items():
benchmark.totalTime[key] = min(values)
for key, values in iterations.items():
benchmark.iterations[key] = min(values)
return benchmark
@classmethod
def average(self, benchmarks):
benchmark = Benchmark()
totalTime = {}
iterations = {}
average = lambda x : sum(x) / len(x)
for b in benchmarks:
for key, value in b.totalTime.items():
if key not in totalTime:
totalTime[key] = []
totalTime[key].append(value)
for key, value in b.iterations.items():
if key not in iterations:
iterations[key] = []
iterations[key].append(value)
for key, values in totalTime.items():
benchmark.totalTime[key] = average(values)
for key, values in iterations.items():
benchmark.iterations[key] = average(values)
return benchmark