Blame benchtests/scripts/compare_strings.py

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))