Skip to content

Commit 1313114

Browse files
authored
More functionality for the combiner performance test suite (#17572)
1 parent c9d7866 commit 1313114

17 files changed

+1476
-325
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import sys
5+
import collections
6+
import base64
7+
import argparse
8+
9+
def gen_chart(ref_time, graph_time, llvm_time = None):
10+
chars = '▏▎▍▌▋▊▉█'
11+
fullwidth = chars[-2] # for separators between 1-second blocks
12+
13+
def draw_line(val, hue, title, comment):
14+
val = val / 1000.0
15+
16+
if hue is None:
17+
color = 'darkgray'
18+
else:
19+
color = f'hsl({hue} 80% 40%)'
20+
21+
out = f'<div style="color:{color};font-family:monospace;">'
22+
if title:
23+
out += f'<span style="width:4em;display:inline-block">{title}</span>'
24+
25+
for _ in range(int(val)):
26+
out += fullwidth
27+
28+
remainder = int(round((val - int(val)) * 7.0))
29+
if remainder > 0:
30+
out += chars[remainder - 1]
31+
if comment:
32+
out += '&nbsp;'
33+
out += comment
34+
out += '</div>\n'
35+
36+
return out
37+
38+
out = ''
39+
out += draw_line(ref_time, None, 'C++', None)
40+
41+
if ref_time == 0:
42+
hue = None
43+
else:
44+
hue = 120 - int(120.0 * ((graph_time / ref_time) - 0.5) / 1.5) # Map [0.5, 2] -> [120, 0]
45+
hue = max(0, min(hue, 120)) # clamp to the [0, 120] range; hue 0 = red, hue 120 = green
46+
47+
def add_colored_bar(ref_value, result_value, title):
48+
if result_value:
49+
shame_ratio = result_value / ref_value
50+
comment = 'x %.1f' % shame_ratio
51+
else:
52+
comment = None
53+
return draw_line(result_value, hue, title, comment)
54+
55+
out += add_colored_bar(ref_time, graph_time, 'Graph')
56+
if llvm_time is not None:
57+
out += add_colored_bar(ref_time, llvm_time, 'LLVM')
58+
59+
return out
60+
61+
def sample_rows(sample):
62+
return sample['rowsPerRun'] * sample['numRuns']
63+
64+
def format_large_num(num):
65+
if num >= 1000000:
66+
sf = 1000000.0
67+
suffix = 'M'
68+
elif num >= 1000:
69+
sf = 1000.0
70+
suffix = 'K'
71+
else:
72+
sf = 1
73+
suffix = ''
74+
75+
formatted = '%.02f' % (num / sf)
76+
if '.' in formatted:
77+
while formatted.endswith('0'):
78+
formatted = formatted[:-1]
79+
if formatted.endswith('.'):
80+
formatted = formatted[:-1]
81+
formatted += suffix
82+
return formatted
83+
84+
def format_time(ms):
85+
if ms is None:
86+
return ' '
87+
return '%.2f' % (ms / 1000.0)
88+
89+
def format_mem(bytez):
90+
return '%.1f' % (bytez / (1024.0 * 1024.0))
91+
92+
def do_merge_llvm(samples):
93+
sorted_samples = sorted(samples, key=lambda sample: sample.get('llvm', False))
94+
output_samples = []
95+
index = {}
96+
97+
for sample in sorted_samples:
98+
is_llvm = sample.get('llvm', False)
99+
key = (sample['testName'], sample['numKeys'], sample_rows(sample), sample.get('spilling', False), sample.get('blockSize', 0), sample.get('combinerMemLimit', 0))
100+
if key in index and not is_llvm:
101+
raise Exception('Attempted to merge two non-LLVM result samples, key = %s' % repr(key))
102+
if key not in index and is_llvm:
103+
raise Exception('Non-LLVM result sample is missing, key = %s' % repr(key))
104+
105+
if is_llvm:
106+
gen_time = sample['generatorTime']
107+
result_time = sample['resultTime']
108+
result_time_or_zero = result_time - gen_time if gen_time <= result_time else 0
109+
index[key]['llvmCleanTime'] = result_time_or_zero
110+
else:
111+
index[key] = sample
112+
output_samples.append(sample)
113+
114+
return output_samples
115+
116+
def do_format(merge_llvm):
117+
per_section = collections.defaultdict(list)
118+
119+
all_samples = []
120+
for line in sys.stdin:
121+
line = line.strip()
122+
if not line:
123+
continue
124+
sample = json.loads(line)
125+
all_samples.append(sample)
126+
127+
if merge_llvm:
128+
all_samples = do_merge_llvm(all_samples)
129+
130+
for sample in all_samples:
131+
section_name = (sample['testName'], sample_rows(sample), sample.get('llvm', False), sample.get('spilling', False), sample.get('blockSize', 0), sample.get('combinerMemLimit', 0))
132+
per_section[section_name].append(sample)
133+
134+
for _, samples in per_section.items():
135+
combiner_name = samples[0]['testName']
136+
num_rows = sample_rows(samples[0])
137+
num_rows_formatted = format_large_num(num_rows)
138+
139+
has_llvm_column = any(('llvmCleanTime' in sample for sample in samples))
140+
141+
traits = []
142+
if samples[0].get('llvm', False):
143+
traits.append('LLVM')
144+
if samples[0].get('spilling', False):
145+
traits.append('spilling')
146+
if samples[0].get('blockSize', 0):
147+
traits.append(f'{samples[0]["blockSize"]} rows per block')
148+
memlimit = samples[0].get('combinerMemLimit', 0)
149+
if memlimit and combiner_name == 'WideCombiner':
150+
memlimit_formatted = format_mem(memlimit)
151+
traits.append(f'{memlimit_formatted} MB RAM limit')
152+
traits.append(f'{num_rows_formatted} input rows')
153+
traits_str = ', '.join(traits)
154+
155+
own_times = []
156+
for sample in samples:
157+
own_times.append(sample['generatorTime'])
158+
own_times.sort()
159+
median_own_time = format_time(own_times[len(own_times) // 2])
160+
161+
print(f'##### {combiner_name}, {traits_str}\n')
162+
print(f'Input generator elapsed time: {median_own_time}с\n')
163+
print('::: html\n<table><tr>\n')
164+
headers = [
165+
'Shame ratio',
166+
'Distinct keys',
167+
'Graph time (s)',
168+
'Reference C++ impl time (s)',
169+
]
170+
if has_llvm_column:
171+
headers += [
172+
'LLVM time (s)',
173+
]
174+
headers += [
175+
'MaxRSS delta, MB',
176+
'Bytes per key',
177+
]
178+
print(''.join(['<th>%s</th>' % item for item in headers]) + '\n')
179+
180+
for sample in samples:
181+
gen_time = sample['generatorTime']
182+
result_time = sample['resultTime']
183+
ref_time = sample['refTime']
184+
result_time_or_zero = result_time - gen_time if gen_time <= result_time else 0
185+
ref_time_or_zero = ref_time - gen_time if gen_time <= ref_time else 0
186+
llvm_time_or_zero = sample.get('llvmCleanTime', None)
187+
188+
shame_ratio = 0
189+
if ref_time_or_zero > 0:
190+
shame_ratio = result_time_or_zero / ref_time_or_zero
191+
192+
cols = []
193+
if llvm_time_or_zero is not None:
194+
cols.append(gen_chart(ref_time_or_zero, result_time_or_zero, llvm_time_or_zero))
195+
else:
196+
cols.append(gen_chart(ref_time_or_zero, result_time_or_zero))
197+
198+
cols.append(format_large_num(sample['numKeys']))
199+
cols.append(format_time(result_time_or_zero))
200+
cols.append(format_time(ref_time_or_zero))
201+
if has_llvm_column:
202+
cols.append(format_time(llvm_time_or_zero))
203+
cols.append(format_mem(sample['maxRssDelta']))
204+
bytes_per_key = sample['maxRssDelta'] // sample['numKeys']
205+
206+
cols.append(str(bytes_per_key) if 0 < bytes_per_key < 10000 else ' ')
207+
print('<tr>' + ''.join(['<td>%s</td>' % col for col in cols]) + '</tr>\n')
208+
209+
print('</table>\n:::\n')
210+
211+
def main():
212+
parser = argparse.ArgumentParser()
213+
parser.add_argument('--separate-llvm', action='store_true')
214+
args = parser.parse_args()
215+
216+
do_format(not args.separate_llvm)
217+
218+
if __name__ == '__main__':
219+
main()

0 commit comments

Comments
 (0)