forked from heavyai/heavydb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanalyze_benchmark.py
384 lines (341 loc) · 13.8 KB
/
analyze_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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
import json
import sys
from os import listdir
import os.path
import getopt
# loads a single benchmark json at a time, and can access its data
class BenchmarkLoader:
def __init__(self, dir_name, filename_list):
self.dir_name = dir_name
self.filename_list = filename_list
self.data = []
# reading the benchmark json file
def load(self, bench_filename):
assert bench_filename in self.filename_list
with open(self.dir_name + bench_filename) as json_file:
# only load those queries that were successful
filtered_input_data = filter(
lambda experiment: experiment["succeeded"] is True,
json.load(json_file),
)
# sort queries based on their IDs
self.data = sorted(
filtered_input_data,
key=lambda experiment: experiment["results"]["query_id"],
)
def getFrontAttribute(self, attribute):
if self.data:
return self.data[0]["results"][attribute]
else:
return "None"
def getExistingDataRunLabel(self):
return self.getFrontAttribute("run_label")
def getGpuName(self):
return self.getFrontAttribute("run_gpu_name")
def getRunTableName(self):
return self.getFrontAttribute("run_table")
# return a list of the attribute, from queries in query_names, stored in self.data
def fetchAttribute(self, attribute, query_names):
result = []
for query in query_names:
for experiment in self.data:
assert attribute in experiment["results"], (
attribute + " is not a valid attribute."
)
if query == experiment["results"]["query_id"]:
result.append(experiment["results"][attribute])
break
return result
def fetchQueryNames(self):
result = []
for experiment in self.data:
result.append(experiment["results"]["query_id"])
return result
class BenchAnalyzer:
def __init__(self, ref, sample, attribute):
assert isinstance(ref, BenchmarkLoader)
assert isinstance(sample, BenchmarkLoader)
self.__header_info = [ref.getFrontAttribute("query_group"), attribute]
self.__label_name_ref = ref.fetchQueryNames()
self.__label_name_sample = sample.fetchQueryNames()
self.__missing_queries_ref = []
self.__missing_queries_sample = []
self.collectMissingQueries()
assert self.__label_name_ref == self.__label_name_sample
self.__attribute_ref = ref.fetchAttribute(
attribute, self.__label_name_ref
)
self.__attribute_sample = sample.fetchAttribute(
attribute, self.__label_name_sample
)
# collects all those queries that does not exist in both of the results
def collectMissingQueries(self):
for query in self.__label_name_ref:
if query not in self.__label_name_sample:
self.__missing_queries_sample.append(query)
self.__label_name_ref.remove(query)
for query in self.__label_name_sample:
if query not in self.__label_name_ref:
self.__missing_queries_ref.append(query)
self.__label_name_sample.remove(query)
def printHeader(self):
for h in self.__header_info:
print(" " + h, end="")
def findAnomaliesRatio(self, epsilon):
found = False
speedup = compute_speedup(
self.__attribute_ref, self.__attribute_sample
)
print("Differences outside of %2.0f%%: " % (epsilon * 100), end="")
self.printHeader()
for i in range(len(speedup)):
if abs(speedup[i] - 1.0) > epsilon:
if found == False:
found = True
print(
"\n%s: reference = %.2f ms, sample = %.2f ms, speedup = %.2fx"
% (
self.__label_name_ref[i],
self.__attribute_ref[i],
self.__attribute_sample[i],
speedup[i],
),
end="",
)
if found == False:
print(": None", end="")
if self.__missing_queries_ref:
print("\n*** Missing queries from reference: ", end="")
for query in self.__missing_queries_ref:
print(query + " ", end="")
if self.__missing_queries_sample:
print("\n*** Missing queries from sample: ", end="")
for query in self.__missing_queries_sample:
print(query + " ", end="")
print(
"\n======================================================================="
)
def compute_speedup(x, y):
result = []
zipped = list(zip(x, y))
for q in zipped:
result.append(q[0] / q[1])
return result
class PrettyPrint:
"""
This class is just used to print out the benchmark results into the terminal.
By default, it is used for cross comparison of the results between a reference
branch (ref) and a sample branch (sample); for a particular attribute, all elements
within each branch are shown as well as the speedup (sample / ref).
If cross_comparison is disabled, then it just shows the result for the ref branch.
"""
def __init__(
self,
ref,
sample,
attribute,
cross_comparison=True,
num_items_per_line=5,
):
self.__cross_comparison = cross_comparison
assert isinstance(ref, BenchmarkLoader)
if cross_comparison:
assert isinstance(sample, BenchmarkLoader)
self.__header_info = [
ref.getRunTableName(),
attribute,
ref.getGpuName(),
]
self.__num_items_per_line = num_items_per_line
self.__label_name_ref = ref.fetchQueryNames()
if cross_comparison:
self.__label_name_sample = sample.fetchQueryNames()
assert self.__label_name_ref == self.__label_name_sample
self.__missing_queries_ref = []
self.__missing_queries_sample = []
if cross_comparison:
self.collectMissingQueries()
self.__attribute_ref = ref.fetchAttribute(
attribute, self.__label_name_ref
)
if cross_comparison:
self.__attribute_sample = sample.fetchAttribute(
attribute, self.__label_name_sample
)
self.__ref_line_count = 0
self.__sample_line_count = 0
# collects all those queries that does not exist in both of the results
def collectMissingQueries(self):
for query in self.__label_name_ref:
if query not in self.__label_name_sample:
self.__missing_queries_sample.append(query)
self.__label_name_ref.remove(query)
for query in self.__label_name_sample:
if query not in self.__label_name_ref:
self.__missing_queries_ref.append(query)
self.__label_name_sample.remove(query)
def printSolidLine(self, pattern):
for i in range(self.__num_items_per_line + 1):
for j in range(11):
print(pattern, end="")
print("")
def printHeader(self):
for h in self.__header_info:
print("\t" + h)
self.printSolidLine("=")
def getRefElementsPerLine(self):
return self.__ref_line_count * self.__num_items_per_line
def printLine(self, array):
begin = self.getRefElementsPerLine()
end = self.getRefElementsPerLine() + self.__num_items_per_line
for i in range(begin, min(end, len(self.__attribute_ref))):
if isinstance(array[i], float):
print("%10.2f" % (array[i]), end="")
elif isinstance(array[i], str):
print("%10s" % (array[i]), end="")
else:
assert False
print("")
def printAttribute(self):
self.printHeader()
ref_count = len(self.__attribute_ref)
while self.getRefElementsPerLine() < ref_count:
print("%10s" % "Queries", end="")
self.printLine(self.__label_name_ref)
self.printSolidLine("-")
print("%10s" % "Reference", end="")
self.printLine(self.__attribute_ref)
if self.__cross_comparison:
print("%10s" % "Sample", end="")
self.printLine(self.__attribute_sample)
print("%10s" % "Speedup", end="")
self.printLine(
compute_speedup(
self.__attribute_ref, self.__attribute_sample
)
)
self.printSolidLine("=")
self.__ref_line_count += 1
print("\n\n\n")
def main(argv):
try:
opts, args = getopt.getopt(
argv,
"hs:r:e:a:p",
[
"help",
"sample=",
"reference=",
"epsilon=",
"attribute=",
"print",
],
)
except getopt.GetOptError:
print(
"python3 analyze-benchmark.py -s <sample dir> -r <reference dir> -e <epsilon> -a <attribute> -p"
)
sys.exit(2)
dir_artifact_sample = ""
dir_artifact_ref = ""
epsilon = 0.05
query_attribute = (
"query_exec_trimmed_avg"
) # default attribute to use for benchmark comparison
to_print = False # printing all the results, disabled by default
for opt, arg in opts:
if opt in ("-h", "--help"):
print(
"""
-s/--sample:\t\t\t directory of the results for the benchmarked sample branch
-r/--reference:\t\t\t directory of the results for the benchmarked reference branch
-e/--epsilon:\t\t\t ratio tolerance for reporting results outside this range
-a/--attribute:\t\t\t attribute to be used for benchmark comparison (default: query_total_avg)
-p/--print:\t\t\t\t print all the results
"""
)
sys.exit()
else:
if opt in ("-s", "--sample"):
dir_artifact_sample = arg
assert os.path.isdir(dir_artifact_sample)
elif opt in ("-r", "--reference"):
dir_artifact_ref = arg
assert os.path.isdir(dir_artifact_ref)
elif opt in ("-e", "--epsilon"):
epsilon = float(arg)
elif opt in ("-a", "--attribute"):
query_attribute = arg
elif opt in ("-p", "--print"):
to_print = True
assert dir_artifact_ref is not ""
assert dir_artifact_sample is not ""
assert epsilon <= 1
GPU_list_ref = listdir(dir_artifact_ref)
GPU_list_sample = listdir(dir_artifact_sample)
for gpu in GPU_list_ref:
dir_name_ref = dir_artifact_ref + "/" + gpu + "/Benchmarks"
filename_list_ref = listdir(dir_name_ref)
dir_name_ref += "/"
refBench = BenchmarkLoader(dir_name_ref, filename_list_ref)
if gpu in GPU_list_sample:
dir_name_sample = dir_artifact_sample + "/" + gpu + "/Benchmarks"
filename_list_sample = listdir(dir_name_sample)
dir_name_sample += "/"
sampleBench = BenchmarkLoader(
dir_name_sample, filename_list_sample
)
first_header = True
for index in range(len(filename_list_ref)):
refBench.load(filename_list_ref[index])
if filename_list_ref[index] in filename_list_sample:
sampleBench.load(filename_list_ref[index])
if first_header:
print(
"\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
)
print("++++ " + sampleBench.getGpuName())
print(
"++++ reference("
+ refBench.getFrontAttribute("run_label")
+ "): "
+ refBench.getFrontAttribute("run_version")
)
print(
"++++ sample("
+ sampleBench.getFrontAttribute("run_label")
+ "): "
+ sampleBench.getFrontAttribute("run_version")
)
print(
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
)
first_header = False
analyzer = BenchAnalyzer(
refBench, sampleBench, query_attribute
)
analyzer.findAnomaliesRatio(epsilon)
if to_print:
printer = PrettyPrint(
refBench, sampleBench, query_attribute
)
printer.printAttribute()
else:
print(
"No sample results for table "
+ refBench.getRunTableName()
+ " were found."
)
print(
"======================================================================="
)
else:
print(
"\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
)
print("++++ No sample results for GPU " + gpu + " were found.")
print(
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
)
if __name__ == "__main__":
main(sys.argv[1:])