|
Packit |
6c4009 |
#!/usr/bin/python
|
|
Packit |
6c4009 |
# Copyright (C) 2017-2018 Free Software Foundation, Inc.
|
|
Packit |
6c4009 |
# This file is part of the GNU C Library.
|
|
Packit |
6c4009 |
#
|
|
Packit |
6c4009 |
# The GNU C Library is free software; you can redistribute it and/or
|
|
Packit |
6c4009 |
# modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6c4009 |
# License as published by the Free Software Foundation; either
|
|
Packit |
6c4009 |
# version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6c4009 |
#
|
|
Packit |
6c4009 |
# The GNU C Library is distributed in the hope that it will be useful,
|
|
Packit |
6c4009 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6c4009 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6c4009 |
# Lesser General Public License for more details.
|
|
Packit |
6c4009 |
#
|
|
Packit |
6c4009 |
# You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6c4009 |
# License along with the GNU C Library; if not, see
|
|
Packit |
6c4009 |
# <http://www.gnu.org/licenses/>.
|
|
Packit |
6c4009 |
"""Compare results of string functions
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Given a string benchmark result file, print a table with comparisons with a
|
|
Packit |
6c4009 |
baseline. The baseline is the first function, which typically is the builtin
|
|
Packit |
6c4009 |
function.
|
|
Packit |
6c4009 |
"""
|
|
Packit |
6c4009 |
import matplotlib as mpl
|
|
Packit |
6c4009 |
mpl.use('Agg')
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
import sys
|
|
Packit |
6c4009 |
import os
|
|
Packit |
6c4009 |
import json
|
|
Packit |
6c4009 |
import pylab
|
|
Packit |
6c4009 |
import argparse
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
try:
|
|
Packit |
6c4009 |
import jsonschema as validator
|
|
Packit |
6c4009 |
except ImportError:
|
|
Packit |
6c4009 |
print('Could not find jsonschema module.')
|
|
Packit |
6c4009 |
raise
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
def parse_file(filename, schema_filename):
|
|
Packit |
6c4009 |
try:
|
|
Packit |
6c4009 |
with open(schema_filename, 'r') as schemafile:
|
|
Packit |
6c4009 |
schema = json.load(schemafile)
|
|
Packit |
6c4009 |
with open(filename, 'r') as benchfile:
|
|
Packit |
6c4009 |
bench = json.load(benchfile)
|
|
Packit |
6c4009 |
validator.validate(bench, schema)
|
|
Packit |
6c4009 |
return bench
|
|
Packit |
6c4009 |
except FileNotFoundError:
|
|
Packit |
6c4009 |
sys.stderr.write('Invalid input file %s.\n' % filename)
|
|
Packit |
6c4009 |
sys.exit(os.EX_NOINPUT)
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
def draw_graph(f, v, ifuncs, results):
|
|
Packit |
6c4009 |
"""Plot graphs for functions
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Plot line graphs for each of the ifuncs
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Args:
|
|
Packit |
6c4009 |
f: Function name
|
|
Packit |
6c4009 |
v: Benchmark variant for the function.
|
|
Packit |
6c4009 |
ifuncs: List of ifunc names
|
|
Packit |
6c4009 |
results: Dictionary of results for each test criterion
|
|
Packit |
6c4009 |
"""
|
|
Packit |
6c4009 |
print('Generating graph for %s, variant \'%s\'' % (f, v))
|
|
Packit |
6c4009 |
xkeys = results.keys()
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
pylab.clf()
|
|
Packit |
6c4009 |
fig = pylab.figure(frameon=False)
|
|
Packit |
6c4009 |
fig.set_size_inches(32, 18)
|
|
Packit |
6c4009 |
pylab.ylabel('Performance improvement from base')
|
|
Packit |
6c4009 |
X = range(len(xkeys))
|
|
Packit |
6c4009 |
pylab.xticks(X, xkeys)
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
i = 0
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
while i < len(ifuncs):
|
|
Packit |
6c4009 |
Y = [results[k][i] for k in xkeys]
|
|
Packit |
6c4009 |
lines = pylab.plot(X, Y, label=':'+ifuncs[i])
|
|
Packit |
6c4009 |
i = i + 1
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
pylab.legend()
|
|
Packit |
6c4009 |
pylab.grid()
|
|
Packit |
6c4009 |
pylab.savefig('%s-%s.png' % (f, v), bbox_inches='tight')
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
def process_results(results, attrs, funcs, base_func, graph, no_diff, no_header):
|
|
Packit |
6c4009 |
""" Process results and print them
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Args:
|
|
Packit |
6c4009 |
results: JSON dictionary of results
|
|
Packit |
6c4009 |
attrs: Attributes that form the test criteria
|
|
Packit |
6c4009 |
funcs: Functions that are selected
|
|
Packit |
6c4009 |
"""
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
for f in results['functions'].keys():
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
v = results['functions'][f]['bench-variant']
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
selected = {}
|
|
Packit |
6c4009 |
index = 0
|
|
Packit |
6c4009 |
base_index = 0
|
|
Packit |
6c4009 |
if funcs:
|
|
Packit |
6c4009 |
ifuncs = []
|
|
Packit |
6c4009 |
first_func = True
|
|
Packit |
6c4009 |
for i in results['functions'][f]['ifuncs']:
|
|
Packit |
6c4009 |
if i in funcs:
|
|
Packit |
6c4009 |
if first_func:
|
|
Packit |
6c4009 |
base_index = index
|
|
Packit |
6c4009 |
first_func = False
|
|
Packit |
6c4009 |
selected[index] = 1
|
|
Packit |
6c4009 |
ifuncs.append(i)
|
|
Packit |
6c4009 |
else:
|
|
Packit |
6c4009 |
selected[index] = 0
|
|
Packit |
6c4009 |
index += 1
|
|
Packit |
6c4009 |
else:
|
|
Packit |
6c4009 |
ifuncs = results['functions'][f]['ifuncs']
|
|
Packit |
6c4009 |
for i in ifuncs:
|
|
Packit |
6c4009 |
selected[index] = 1
|
|
Packit |
6c4009 |
index += 1
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if base_func:
|
|
Packit |
6c4009 |
try:
|
|
Packit |
6c4009 |
base_index = results['functions'][f]['ifuncs'].index(base_func)
|
|
Packit |
6c4009 |
except ValueError:
|
|
Packit |
6c4009 |
sys.stderr.write('Invalid -b "%s" parameter. Options: %s.\n' %
|
|
Packit |
6c4009 |
(base_func, ', '.join(results['functions'][f]['ifuncs'])))
|
|
Packit |
6c4009 |
sys.exit(os.EX_DATAERR)
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if not no_header:
|
|
Packit |
6c4009 |
print('Function: %s' % f)
|
|
Packit |
6c4009 |
print('Variant: %s' % v)
|
|
Packit |
6c4009 |
print("%36s%s" % (' ', '\t'.join(ifuncs)))
|
|
Packit |
6c4009 |
print("=" * 120)
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
graph_res = {}
|
|
Packit |
6c4009 |
for res in results['functions'][f]['results']:
|
|
Packit |
6c4009 |
try:
|
|
Packit |
6c4009 |
attr_list = ['%s=%s' % (a, res[a]) for a in attrs]
|
|
Packit |
6c4009 |
except KeyError as ke:
|
|
Packit |
6c4009 |
sys.stderr.write('Invalid -a %s parameter. Options: %s.\n'
|
|
Packit |
6c4009 |
% (ke, ', '.join([a for a in res.keys() if a != 'timings'])))
|
|
Packit |
6c4009 |
sys.exit(os.EX_DATAERR)
|
|
Packit |
6c4009 |
i = 0
|
|
Packit |
6c4009 |
key = ', '.join(attr_list)
|
|
Packit |
6c4009 |
sys.stdout.write('%36s: ' % key)
|
|
Packit |
6c4009 |
graph_res[key] = res['timings']
|
|
Packit |
6c4009 |
for t in res['timings']:
|
|
Packit |
6c4009 |
if selected[i]:
|
|
Packit |
6c4009 |
sys.stdout.write ('%12.2f' % t)
|
|
Packit |
6c4009 |
if not no_diff:
|
|
Packit |
6c4009 |
if i != base_index:
|
|
Packit |
6c4009 |
base = res['timings'][base_index]
|
|
Packit |
6c4009 |
diff = (base - t) * 100 / base
|
|
Packit |
6c4009 |
sys.stdout.write (' (%6.2f%%)' % diff)
|
|
Packit |
6c4009 |
sys.stdout.write('\t')
|
|
Packit |
6c4009 |
i = i + 1
|
|
Packit |
6c4009 |
print('')
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if graph:
|
|
Packit |
6c4009 |
draw_graph(f, v, results['functions'][f]['ifuncs'], graph_res)
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
def main(args):
|
|
Packit |
6c4009 |
"""Program Entry Point
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Take a string benchmark output file and compare timings.
|
|
Packit |
6c4009 |
"""
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
base_func = None
|
|
Packit |
6c4009 |
filename = args.input
|
|
Packit |
6c4009 |
schema_filename = args.schema
|
|
Packit |
6c4009 |
base_func = args.base
|
|
Packit |
6c4009 |
attrs = args.attributes.split(',')
|
|
Packit |
6c4009 |
if args.functions:
|
|
Packit |
6c4009 |
funcs = args.functions.split(',')
|
|
Packit |
6c4009 |
if base_func and not base_func in funcs:
|
|
Packit |
6c4009 |
print('Baseline function (%s) not found.' % base_func)
|
|
Packit |
6c4009 |
sys.exit(os.EX_DATAERR)
|
|
Packit |
6c4009 |
else:
|
|
Packit |
6c4009 |
funcs = None
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
results = parse_file(args.input, args.schema)
|
|
Packit |
6c4009 |
process_results(results, attrs, funcs, base_func, args.graph, args.no_diff, args.no_header)
|
|
Packit |
6c4009 |
return os.EX_OK
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if __name__ == '__main__':
|
|
Packit |
6c4009 |
parser = argparse.ArgumentParser()
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
# The required arguments.
|
|
Packit |
6c4009 |
req = parser.add_argument_group(title='required arguments')
|
|
Packit |
6c4009 |
req.add_argument('-a', '--attributes', required=True,
|
|
Packit |
6c4009 |
help='Comma separated list of benchmark attributes.')
|
|
Packit |
6c4009 |
req.add_argument('-i', '--input', required=True,
|
|
Packit |
6c4009 |
help='Input JSON benchmark result file.')
|
|
Packit |
6c4009 |
req.add_argument('-s', '--schema', required=True,
|
|
Packit |
6c4009 |
help='Schema file to validate the result file.')
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
# Optional arguments.
|
|
Packit |
6c4009 |
parser.add_argument('-f', '--functions',
|
|
Packit |
6c4009 |
help='Comma separated list of functions.')
|
|
Packit |
6c4009 |
parser.add_argument('-b', '--base',
|
|
Packit |
6c4009 |
help='IFUNC variant to set as baseline.')
|
|
Packit |
6c4009 |
parser.add_argument('-g', '--graph', action='store_true',
|
|
Packit |
6c4009 |
help='Generate a graph from results.')
|
|
Packit |
6c4009 |
parser.add_argument('--no-diff', action='store_true',
|
|
Packit |
6c4009 |
help='Do not print the difference from baseline.')
|
|
Packit |
6c4009 |
parser.add_argument('--no-header', action='store_true',
|
|
Packit |
6c4009 |
help='Do not print the header.')
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
args = parser.parse_args()
|
|
Packit |
6c4009 |
sys.exit(main(args))
|