Skip to content

Commit 885e783

Browse files
committed
[libc++] Allow sorting by a few criteria in compare-benchmarks
1 parent d2478e2 commit 885e783

File tree

1 file changed

+31
-7
lines changed

1 file changed

+31
-7
lines changed

libcxx/utils/compare-benchmarks

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,7 @@ def plain_text_comparison(data, metric, baseline_name=None, candidate_name=None)
6363
"""
6464
Create a tabulated comparison of the baseline and the candidate for the given metric.
6565
"""
66-
# Compute additional info in new columns. In text mode, we can assume that we are
67-
# comparing exactly two data sets (suffixed _0 and _1).
68-
data['difference'] = data[f'{metric}_1'] - data[f'{metric}_0']
69-
data['percent'] = 100 * (data['difference'] / data[f'{metric}_0'])
70-
71-
data = data.replace(numpy.nan, None).sort_values(by='benchmark') # avoid NaNs in tabulate output
66+
data = data.replace(numpy.nan, None) # avoid NaNs in tabulate output
7267
headers = ['Benchmark', baseline_name, candidate_name, 'Difference', '% Difference']
7368
fmt = (None, '.2f', '.2f', '.2f', '.2f')
7469
table = data[['benchmark', f'{metric}_0', f'{metric}_1', 'difference', 'percent']].set_index('benchmark')
@@ -78,7 +73,7 @@ def create_chart(data, metric, subtitle=None, series_names=None):
7873
"""
7974
Create a bar chart comparing the given metric across the provided series.
8075
"""
81-
data = data.sort_values(by='benchmark').rename(columns={f'{metric}_{i}': series_names[i] for i in range(len(series_names))})
76+
data = data.rename(columns={f'{metric}_{i}': series_names[i] for i in range(len(series_names))})
8277
title = ' vs '.join(series_names)
8378
figure = plotly.express.bar(data, title=title, subtitle=subtitle, x='benchmark', y=series_names, barmode='group')
8479
figure.update_layout(xaxis_title='', yaxis_title='', legend_title='')
@@ -102,6 +97,15 @@ def main(argv):
10297
parser.add_argument('--filter', type=str, required=False,
10398
help='An optional regular expression used to filter the benchmarks included in the comparison. '
10499
'Only benchmarks whose names match the regular expression will be included.')
100+
parser.add_argument('--sort', type=str, required=False, default='benchmark',
101+
choices=['benchmark', 'baseline', 'candidate', 'percent_diff'],
102+
help='Optional sorting criteria for displaying results. By default, results are displayed in '
103+
'alphabetical order of the benchmark. Supported sorting criteria are: '
104+
'`benchmark` (sort using the alphabetical name of the benchmark), '
105+
'`baseline` (sort using the absolute number of the baseline run), '
106+
'`candidate` (sort using the absolute number of the candidate run), '
107+
'and `percent_diff` (sort using the percent difference between the baseline and the candidate). '
108+
'Note that when more than two input files are compared, the only valid sorting order is `benchmark`.')
105109
parser.add_argument('--format', type=str, choices=['text', 'chart'], default='text',
106110
help='Select the output format. `text` generates a plain-text comparison in tabular form, and `chart` '
107111
'generates a self-contained HTML graph that can be opened in a browser. The default is `text`.')
@@ -116,6 +120,8 @@ def main(argv):
116120
'This option cannot be used with the plain text output.')
117121
args = parser.parse_args(argv)
118122

123+
# Validate arguments (the values admissible for various arguments depend on other
124+
# arguments, the number of inputs, etc)
119125
if args.format == 'text':
120126
if len(args.files) != 2:
121127
parser.error('--format=text requires exactly two input files to compare')
@@ -124,6 +130,9 @@ def main(argv):
124130
if args.open:
125131
parser.error('Passing --open makes no sense with --format=text')
126132

133+
if len(args.files) != 2 and args.sort != 'benchmark':
134+
parser.error('Using any sort order other than `benchmark` requires exactly two input files.')
135+
127136
if args.series_names is None:
128137
args.series_names = ['Baseline']
129138
if len(args.files) == 2:
@@ -142,10 +151,25 @@ def main(argv):
142151
# Join the inputs into a single dataframe
143152
data = functools.reduce(lambda a, b: a.merge(b, how='outer', on='benchmark'), inputs)
144153

154+
# If we have exactly two data sets, compute additional info in new columns
155+
if len(lnt_inputs) == 2:
156+
data['difference'] = data[f'{args.metric}_1'] - data[f'{args.metric}_0']
157+
data['percent'] = 100 * (data['difference'] / data[f'{args.metric}_0'])
158+
145159
if args.filter is not None:
146160
keeplist = [b for b in data['benchmark'] if re.search(args.filter, b) is not None]
147161
data = data[data['benchmark'].isin(keeplist)]
148162

163+
# Sort the data by the appropriate criteria
164+
if args.sort == 'benchmark':
165+
data = data.sort_values(by='benchmark')
166+
elif args.sort == 'baseline':
167+
data = data.sort_values(by=f'{args.metric}_0')
168+
elif args.sort == 'candidate':
169+
data = data.sort_values(by=f'{args.metric}_1')
170+
elif args.sort == 'percent_diff':
171+
data = data.sort_values(by=f'percent')
172+
149173
if args.format == 'chart':
150174
figure = create_chart(data, args.metric, subtitle=args.subtitle, series_names=args.series_names)
151175
do_open = args.output is None or args.open

0 commit comments

Comments
 (0)