Skip to content

Commit fce3b18

Browse files
committed
Add report generator
1 parent 081b94f commit fce3b18

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ __work*
1212
dataset
1313
data
1414
*.csv
15+
16+
# Benchmark reports
17+
reports
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"align": [
3+
"algorithm",
4+
"stage",
5+
"input_data:data_order",
6+
"input_data:data_type",
7+
"input_data:dataset_name",
8+
"input_data:rows",
9+
"input_data:columns",
10+
"input_data:classes",
11+
"input_data:n_clusters"
12+
],
13+
"diff": [
14+
"software_hash",
15+
"hardware_hash",
16+
"measurement_time"
17+
]
18+
}

report_generator.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import os
2+
import sys
3+
import openpyxl
4+
import argparse
5+
import json
6+
import hashlib
7+
from string import ascii_uppercase
8+
import datetime
9+
10+
11+
def get_property(entry, prop):
12+
keys = prop.split(':')
13+
value = entry
14+
for key in keys:
15+
value = value[key]
16+
return value
17+
18+
19+
def result_entries_have_same_values(first_entry, second_entry, props, error_on_missing=True):
20+
res = True
21+
for prop in props:
22+
try:
23+
res = res and (get_property(first_entry, prop) == get_property(second_entry, prop))
24+
except KeyError:
25+
if error_on_missing:
26+
raise KeyError()
27+
return res
28+
29+
30+
def result_entries_are_equal(first_entry, second_entry, config):
31+
props = config['align'] + config['diff']
32+
return result_entries_have_same_values(first_entry, second_entry, props, True)
33+
34+
35+
def result_entries_are_comparable(first_entry, second_entry, config):
36+
props = config['align']
37+
return result_entries_have_same_values(first_entry, second_entry, props, False)
38+
39+
40+
def result_entries_have_same_diff(first_entry, second_entry, config):
41+
props = config['diff']
42+
return result_entries_have_same_values(first_entry, second_entry, props, False)
43+
44+
45+
def results_are_mergeable(first_res, second_res, merging):
46+
hw_hash_equality = first_res['hardware_hash'] == second_res['hardware_hash']
47+
sw_hash_equality = first_res['software_hash'] == second_res['software_hash']
48+
if merging == 'hw_only':
49+
return hw_hash_equality
50+
elif merging == 'sw_only':
51+
return sw_hash_equality
52+
else:
53+
return sw_hash_equality and hw_hash_equality
54+
55+
56+
excel_header_columns = list(ascii_uppercase)
57+
for sym1 in ascii_uppercase:
58+
for sym2 in ascii_uppercase:
59+
excel_header_columns.append(sym1+sym2)
60+
xy_to_excel_cell = lambda x, y: "{}{}".format(excel_header_columns[x], y + 1)
61+
62+
63+
def write_cell(work_sheet, x, y, value):
64+
work_sheet[xy_to_excel_cell(x, y)] = value
65+
66+
67+
def create_list(res_entry, props_list):
68+
line = []
69+
for prop in props_list:
70+
try:
71+
val = get_property(res_entry, prop)
72+
except:
73+
val = ''
74+
line.append(val)
75+
return line
76+
77+
78+
parser = argparse.ArgumentParser()
79+
parser.add_argument("--result-files", type=str, required=True)
80+
parser.add_argument("--generation-config", type=str,
81+
default='configs/default_report_gen_config.json')
82+
parser.add_argument("--merging", type=str, default="none",
83+
choices=("full", "none", "sw_only", "hw_only"))
84+
args = parser.parse_args()
85+
86+
json_results = []
87+
for file_name in args.result_files.split(','):
88+
with open(file_name, 'r') as file:
89+
json_results.append(json.load(file))
90+
91+
with open(args.generation_config, 'r') as file:
92+
gen_config = json.load(file)
93+
94+
wb = openpyxl.Workbook()
95+
ws = wb.active
96+
97+
# compute hash for software and hardware configurations
98+
HASH_LIMIT = 8
99+
for i, json_res in enumerate(json_results):
100+
for ware in ['software', 'hardware']:
101+
h = hashlib.sha256()
102+
h.update(bytes(str(json_res[ware]), encoding='utf-8'))
103+
json_res[f'{ware}_hash'] = h.hexdigest()[:HASH_LIMIT]
104+
105+
# create list of all result entry from all json logs
106+
all_res_entries = []
107+
for i, json_res in enumerate(json_results):
108+
extra_entry_info = json_res.copy()
109+
del extra_entry_info['results']
110+
for res_entry in json_res['results']:
111+
new_res_entry = res_entry.copy()
112+
new_res_entry.update(extra_entry_info)
113+
all_res_entries.append(new_res_entry)
114+
115+
if args.merging != "none":
116+
for i, resi_entry in enumerate(all_res_entries):
117+
already_exist = False
118+
for j, resj_entry in enumerate(all_res_entries):
119+
if i == j or resi_entry == {} or resj_entry == {}:
120+
continue
121+
if result_entries_are_equal(resi_entry, resj_entry, gen_config):
122+
if resi_entry['measurement_time'] < resj_entry['measurement_time']:
123+
resi_entry = resj_entry
124+
resj_entry = {}
125+
126+
while {} in all_res_entries:
127+
all_res_entries.remove({})
128+
129+
diff_combinations = []
130+
for i, res_entry in enumerate(all_res_entries):
131+
already_exist = False
132+
for diff_comb in diff_combinations:
133+
if result_entries_have_same_diff(res_entry, diff_comb, gen_config):
134+
already_exist = True
135+
break
136+
if not already_exist:
137+
diff_comb = res_entry.copy()
138+
diff_combinations.append(diff_comb)
139+
140+
align_combinations = []
141+
for i, res_entry in enumerate(all_res_entries):
142+
already_exist = False
143+
for align_comb in align_combinations:
144+
if result_entries_are_comparable(res_entry, align_comb, gen_config):
145+
already_exist = True
146+
break
147+
if not already_exist:
148+
align_comb = res_entry.copy()
149+
align_combinations.append(align_comb)
150+
151+
HEAD_OFFSET = len(gen_config['diff'])
152+
LEFT_OFFSET = len(gen_config['align'])
153+
154+
stages_splitter = {
155+
'training': ['training', 'computation'],
156+
'inference': ['prediction', 'transformation', 'search']
157+
}
158+
159+
for stage_key in stages_splitter.keys():
160+
ws = wb.create_sheet(title=f'Results ({stage_key})')
161+
162+
for i, col in enumerate(gen_config['align'] + ['time, s']):
163+
write_cell(ws, i, HEAD_OFFSET, col)
164+
165+
for i, row in enumerate(gen_config['diff']):
166+
write_cell(ws, LEFT_OFFSET - 1, i, row)
167+
168+
stage_align_combinations = align_combinations.copy()
169+
170+
for align_comb in stage_align_combinations:
171+
if align_comb['stage'] not in stages_splitter[stage_key]:
172+
stage_align_combinations.remove(align_comb)
173+
174+
for i, align_comb in enumerate(stage_align_combinations):
175+
arr = create_list(align_comb, gen_config['align'])
176+
for j, el in enumerate(arr):
177+
write_cell(ws, j, HEAD_OFFSET + 1 + i, el)
178+
179+
for i, diff_comb in enumerate(diff_combinations):
180+
arr = create_list(diff_comb, gen_config['diff'])
181+
for j, el in enumerate(arr):
182+
write_cell(ws, LEFT_OFFSET + i, j, el)
183+
184+
for i, res_entry in enumerate(all_res_entries):
185+
if res_entry['stage'] not in stages_splitter[stage_key]:
186+
continue
187+
x, y = None, None
188+
for j, align_comb in enumerate(stage_align_combinations):
189+
if result_entries_are_comparable(res_entry, align_comb, gen_config):
190+
y = j
191+
break
192+
for j, diff_comb in enumerate(diff_combinations):
193+
if result_entries_have_same_diff(res_entry, diff_comb, gen_config):
194+
x = j
195+
break
196+
write_cell(ws, LEFT_OFFSET + x, HEAD_OFFSET + 1 + y, res_entry['time[s]'])
197+
198+
# write configs
199+
for i, json_res in enumerate(json_results):
200+
ws = wb.create_sheet(title=f'SW config n{i}_{json_res["software_hash"]}')
201+
ws[xy_to_excel_cell(0, 0)] = f'Software configuration {i} (hash: {json_res["software_hash"]})'
202+
sw_conf = json.dumps(json_res['software'], indent=4).split('\n')
203+
for j in range(len(sw_conf)):
204+
ws[xy_to_excel_cell(0, 1 + j)] = sw_conf[j]
205+
206+
ws = wb.create_sheet(title=f'HW config n{i}_{json_res["hardware_hash"]}')
207+
ws[xy_to_excel_cell(0, 0)] = f'Hardware configuration {i} (hash: {json_res["hardware_hash"]})'
208+
hw_conf = json.dumps(json_res['hardware'], indent=4).split('\n')
209+
for j in range(len(hw_conf)):
210+
ws[xy_to_excel_cell(0, 1 + j)] = hw_conf[j]
211+
212+
os.makedirs('reports', exist_ok=True)
213+
wb.save(f"reports/report_{str(datetime.date.today())}.xlsx")

0 commit comments

Comments
 (0)