From patchwork Mon Mar 24 15:49:34 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 248 X-Patchwork-Delegate: siddhesh@gotplt.org Return-Path: X-Original-To: siddhesh@wilcox.dreamhost.com Delivered-To: siddhesh@wilcox.dreamhost.com Received: from homiemail-mx23.g.dreamhost.com (caibbdcaabja.dreamhost.com [208.113.200.190]) by wilcox.dreamhost.com (Postfix) with ESMTP id E1BBB360097 for ; Mon, 24 Mar 2014 08:49:02 -0700 (PDT) Received: by homiemail-mx23.g.dreamhost.com (Postfix, from userid 14307373) id 9324561E9910A; Mon, 24 Mar 2014 08:49:02 -0700 (PDT) X-Original-To: glibc@patchwork.siddhesh.in Delivered-To: x14307373@homiemail-mx23.g.dreamhost.com Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by homiemail-mx23.g.dreamhost.com (Postfix) with ESMTPS id 6D9F061EC0A05 for ; Mon, 24 Mar 2014 08:49:02 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:date:from:to:cc:subject:message-id:references :mime-version:content-type:in-reply-to; q=dns; s=default; b=VrZu IHvgHHO3Ol5WCqlwUkGm1sUXDkyj4QBSKhmYIPb+IfpMpXNz8GJCAEYb1y4s3Mfr bVtgLK56ARMjM5CQdzIyMylEj0sxgEU8BLlZkn0iRn8kSf3XhOA9ofNDnYPxnL4D ppNistnxYYht7W59206mzwnP92aTzaspSmgxrKw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:date:from:to:cc:subject:message-id:references :mime-version:content-type:in-reply-to; s=default; bh=smOhHHUW6C Ta5EsrkoxVI1iaZLI=; b=frL6GwNLaLk4tFRhpJJB2Lg6h9032yP9AgQS/H92sN DXvA0qVxw+kAkLlIvsqwUI2akI73uryOA4WrSi91I+iH2UVRe01lEowmgEZHXhM3 gKyxlIoFsD3GewNCUVZseTfAm3lTgc2epMB/abmzBsE0a9v1pYfInRvSW9AKxSqh o= Received: (qmail 7184 invoked by alias); 24 Mar 2014 15:49:00 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 7166 invoked by uid 89); 24 Mar 2014 15:48:58 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.7 required=5.0 tests=AWL, BAYES_50, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Date: Mon, 24 Mar 2014 21:19:34 +0530 From: Siddhesh Poyarekar To: Roland McGrath Cc: libc-alpha@sourceware.org Subject: [COMMITTED][BIKESHED] Location for benchmark scripts Message-ID: <20140324154934.GX1850@spoyarek.pnq.redhat.com> References: <20140204075730.GM5209@spoyarek.pnq.redhat.com> <20140204175337.C969F74456@topped-with-meat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20140204175337.C969F74456@topped-with-meat.com> User-Agent: Mutt/1.5.22.1-rc1 (2013-10-16) X-DH-Original-To: glibc@patchwork.siddhesh.in On Tue, Feb 04, 2014 at 09:53:37AM -0800, Roland McGrath wrote: > That sounds fine. The top-level scripts/ is a bit of a dumping ground. > Having benchtests stuff all be somewhere under benchtests/ seems wise. I have pushed this now. Siddhesh commit 27c673b8de3072caf35bc795aa1cd77a7ca18771 Author: Siddhesh Poyarekar Date: Mon Mar 24 21:16:36 2014 +0530 benchtests: Move bench.py to benchtests/scripts/ It makes much more sense to have all benchmarking-related scripts in a single place away from everything else. diff --git a/ChangeLog b/ChangeLog index c2ccddf..ab98fa6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2014-03-24 Siddhesh Poyarekar + + * scripts/bench.py: Moved to ... + * benchtests/scripts/bench.py: ... here. + * benchtests/Makefile ($(objpfx)bench-%.c): Adjust. + 2014-03-24 Andreas Schwab [BZ #16002] diff --git a/benchtests/Makefile b/benchtests/Makefile index 89151b4..b331d1a 100644 --- a/benchtests/Makefile +++ b/benchtests/Makefile @@ -129,5 +129,5 @@ $(objpfx)bench-%.c: %-inputs $(bench-deps) { if [ -n "$($*-INCLUDE)" ]; then \ cat $($*-INCLUDE); \ fi; \ - $(..)scripts/bench.py $(patsubst %-inputs,%,$<); } > $@-tmp + $(.)scripts/bench.py $(patsubst %-inputs,%,$<); } > $@-tmp mv -f $@-tmp $@ diff --git a/benchtests/scripts/bench.py b/benchtests/scripts/bench.py new file mode 100755 index 0000000..e500a33 --- /dev/null +++ b/benchtests/scripts/bench.py @@ -0,0 +1,299 @@ +#!/usr/bin/python +# Copyright (C) 2014 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# . + +"""Benchmark program generator script + +This script takes a function name as input and generates a program using +an input file located in the benchtests directory. The name of the +input file should be of the form foo-inputs where 'foo' is the name of +the function. +""" + +from __future__ import print_function +import sys +import os +import itertools + +# Macro definitions for functions that take no arguments. For functions +# that take arguments, the STRUCT_TEMPLATE, ARGS_TEMPLATE and +# VARIANTS_TEMPLATE are used instead. +DEFINES_TEMPLATE = ''' +#define CALL_BENCH_FUNC(v, i) %(func)s () +#define NUM_VARIANTS (1) +#define NUM_SAMPLES(v) (1) +#define VARIANT(v) FUNCNAME "()" +''' + +# Structures to store arguments for the function call. A function may +# have its inputs partitioned to represent distinct performance +# characteristics or distinct flavors of the function. Each such +# variant is represented by the _VARIANT structure. The ARGS structure +# represents a single set of arguments. +STRUCT_TEMPLATE = ''' +#define CALL_BENCH_FUNC(v, i) %(func)s (%(func_args)s) + +struct args +{ +%(args)s +}; + +struct _variants +{ + const char *name; + int count; + struct args *in; +}; +''' + +# The actual input arguments. +ARGS_TEMPLATE = ''' +struct args in%(argnum)d[%(num_args)d] = { +%(args)s +}; +''' + +# The actual variants, along with macros defined to access the variants. +VARIANTS_TEMPLATE = ''' +struct _variants variants[%(num_variants)d] = { +%(variants)s +}; + +#define NUM_VARIANTS %(num_variants)d +#define NUM_SAMPLES(i) (variants[i].count) +#define VARIANT(i) (variants[i].name) +''' + +# Epilogue for the generated source file. +EPILOGUE = ''' +#define BENCH_FUNC(i, j) ({%(getret)s CALL_BENCH_FUNC (i, j);}) +#define FUNCNAME "%(func)s" +#include "bench-skeleton.c"''' + + +def gen_source(func, directives, all_vals): + """Generate source for the function + + Generate the C source for the function from the values and + directives. + + Args: + func: The function name + directives: A dictionary of directives applicable to this function + all_vals: A dictionary input values + """ + # The includes go in first. + for header in directives['includes']: + print('#include <%s>' % header) + + for header in directives['include-sources']: + print('#include "%s"' % header) + + # Print macros. This branches out to a separate routine if + # the function takes arguments. + if not directives['args']: + print(DEFINES_TEMPLATE % {'func': func}) + outargs = [] + else: + outargs = _print_arg_data(func, directives, all_vals) + + # Print the output variable definitions if necessary. + for out in outargs: + print(out) + + # If we have a return value from the function, make sure it is + # assigned to prevent the compiler from optimizing out the + # call. + if directives['ret']: + print('static %s volatile ret;' % directives['ret']) + getret = 'ret = ' + else: + getret = '' + + print(EPILOGUE % {'getret': getret, 'func': func}) + + +def _print_arg_data(func, directives, all_vals): + """Print argument data + + This is a helper function for gen_source that prints structure and + values for arguments and their variants and returns output arguments + if any are found. + + Args: + func: Function name + directives: A dictionary of directives applicable to this function + all_vals: A dictionary input values + + Returns: + Returns a list of definitions for function arguments that act as + output parameters. + """ + # First, all of the definitions. We process writing of + # CALL_BENCH_FUNC, struct args and also the output arguments + # together in a single traversal of the arguments list. + func_args = [] + arg_struct = [] + outargs = [] + + for arg, i in zip(directives['args'], itertools.count()): + if arg[0] == '<' and arg[-1] == '>': + pos = arg.rfind('*') + if pos == -1: + die('Output argument must be a pointer type') + + outargs.append('static %s out%d;' % (arg[1:pos], i)) + func_args.append(' &out%d' % i) + else: + arg_struct.append(' %s volatile arg%d;' % (arg, i)) + func_args.append('variants[v].in[i].arg%d' % i) + + print(STRUCT_TEMPLATE % {'args' : '\n'.join(arg_struct), 'func': func, + 'func_args': ', '.join(func_args)}) + + # Now print the values. + variants = [] + for (k, vals), i in zip(all_vals.items(), itertools.count()): + out = [' {%s},' % v for v in vals] + + # Members for the variants structure list that we will + # print later. + variants.append(' {"%s(%s)", %d, in%d},' % (func, k, len(vals), i)) + print(ARGS_TEMPLATE % {'argnum': i, 'num_args': len(vals), + 'args': '\n'.join(out)}) + + # Print the variants and the last set of macros. + print(VARIANTS_TEMPLATE % {'num_variants': len(all_vals), + 'variants': '\n'.join(variants)}) + return outargs + + +def _process_directive(d_name, d_val): + """Process a directive. + + Evaluate the directive name and value passed and return the + processed value. This is a helper function for parse_file. + + Args: + d_name: Name of the directive + d_val: The string value to process + + Returns: + The processed value, which may be the string as it is or an object + that describes the directive. + """ + # Process the directive values if necessary. name and ret don't + # need any processing. + if d_name.startswith('include'): + d_val = d_val.split(',') + elif d_name == 'args': + d_val = d_val.split(':') + + # Return the values. + return d_val + + +def parse_file(func): + """Parse an input file + + Given a function name, open and parse an input file for the function + and get the necessary parameters for the generated code and the list + of inputs. + + Args: + func: The function name + + Returns: + A tuple of two elements, one a dictionary of directives and the + other a dictionary of all input values. + """ + all_vals = {} + # Valid directives. + directives = { + 'name': '', + 'args': [], + 'includes': [], + 'include-sources': [], + 'ret': '' + } + + try: + with open('%s-inputs' % func) as f: + for line in f: + # Look for directives and parse it if found. + if line.startswith('##'): + try: + d_name, d_val = line[2:].split(':', 1) + d_name = d_name.strip() + d_val = d_val.strip() + directives[d_name] = _process_directive(d_name, d_val) + except (IndexError, KeyError): + die('Invalid directive: %s' % line[2:]) + + # Skip blank lines and comments. + line = line.split('#', 1)[0].rstrip() + if not line: + continue + + # Otherwise, we're an input. Add to the appropriate + # input set. + cur_name = directives['name'] + all_vals.setdefault(cur_name, []) + all_vals[cur_name].append(line) + except IOError as ex: + die("Failed to open input file (%s): %s" % (ex.filename, ex.strerror)) + + return directives, all_vals + + +def die(msg): + """Exit with an error + + Prints an error message to the standard error stream and exits with + a non-zero status. + + Args: + msg: The error message to print to standard error + """ + print('%s\n' % msg, file=sys.stderr) + sys.exit(os.EX_DATAERR) + + +def main(args): + """Main function + + Use the first command line argument as function name and parse its + input file to generate C source that calls the function repeatedly + for the input. + + Args: + args: The command line arguments with the program name dropped + + Returns: + os.EX_USAGE on error and os.EX_OK on success. + """ + if len(args) != 1: + print('Usage: %s ' % sys.argv[0]) + return os.EX_USAGE + + directives, all_vals = parse_file(args[0]) + gen_source(args[0], directives, all_vals) + return os.EX_OK + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/scripts/bench.py b/scripts/bench.py deleted file mode 100755 index e500a33..0000000 --- a/scripts/bench.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2014 Free Software Foundation, Inc. -# This file is part of the GNU C Library. -# -# The GNU C Library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# The GNU C Library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with the GNU C Library; if not, see -# . - -"""Benchmark program generator script - -This script takes a function name as input and generates a program using -an input file located in the benchtests directory. The name of the -input file should be of the form foo-inputs where 'foo' is the name of -the function. -""" - -from __future__ import print_function -import sys -import os -import itertools - -# Macro definitions for functions that take no arguments. For functions -# that take arguments, the STRUCT_TEMPLATE, ARGS_TEMPLATE and -# VARIANTS_TEMPLATE are used instead. -DEFINES_TEMPLATE = ''' -#define CALL_BENCH_FUNC(v, i) %(func)s () -#define NUM_VARIANTS (1) -#define NUM_SAMPLES(v) (1) -#define VARIANT(v) FUNCNAME "()" -''' - -# Structures to store arguments for the function call. A function may -# have its inputs partitioned to represent distinct performance -# characteristics or distinct flavors of the function. Each such -# variant is represented by the _VARIANT structure. The ARGS structure -# represents a single set of arguments. -STRUCT_TEMPLATE = ''' -#define CALL_BENCH_FUNC(v, i) %(func)s (%(func_args)s) - -struct args -{ -%(args)s -}; - -struct _variants -{ - const char *name; - int count; - struct args *in; -}; -''' - -# The actual input arguments. -ARGS_TEMPLATE = ''' -struct args in%(argnum)d[%(num_args)d] = { -%(args)s -}; -''' - -# The actual variants, along with macros defined to access the variants. -VARIANTS_TEMPLATE = ''' -struct _variants variants[%(num_variants)d] = { -%(variants)s -}; - -#define NUM_VARIANTS %(num_variants)d -#define NUM_SAMPLES(i) (variants[i].count) -#define VARIANT(i) (variants[i].name) -''' - -# Epilogue for the generated source file. -EPILOGUE = ''' -#define BENCH_FUNC(i, j) ({%(getret)s CALL_BENCH_FUNC (i, j);}) -#define FUNCNAME "%(func)s" -#include "bench-skeleton.c"''' - - -def gen_source(func, directives, all_vals): - """Generate source for the function - - Generate the C source for the function from the values and - directives. - - Args: - func: The function name - directives: A dictionary of directives applicable to this function - all_vals: A dictionary input values - """ - # The includes go in first. - for header in directives['includes']: - print('#include <%s>' % header) - - for header in directives['include-sources']: - print('#include "%s"' % header) - - # Print macros. This branches out to a separate routine if - # the function takes arguments. - if not directives['args']: - print(DEFINES_TEMPLATE % {'func': func}) - outargs = [] - else: - outargs = _print_arg_data(func, directives, all_vals) - - # Print the output variable definitions if necessary. - for out in outargs: - print(out) - - # If we have a return value from the function, make sure it is - # assigned to prevent the compiler from optimizing out the - # call. - if directives['ret']: - print('static %s volatile ret;' % directives['ret']) - getret = 'ret = ' - else: - getret = '' - - print(EPILOGUE % {'getret': getret, 'func': func}) - - -def _print_arg_data(func, directives, all_vals): - """Print argument data - - This is a helper function for gen_source that prints structure and - values for arguments and their variants and returns output arguments - if any are found. - - Args: - func: Function name - directives: A dictionary of directives applicable to this function - all_vals: A dictionary input values - - Returns: - Returns a list of definitions for function arguments that act as - output parameters. - """ - # First, all of the definitions. We process writing of - # CALL_BENCH_FUNC, struct args and also the output arguments - # together in a single traversal of the arguments list. - func_args = [] - arg_struct = [] - outargs = [] - - for arg, i in zip(directives['args'], itertools.count()): - if arg[0] == '<' and arg[-1] == '>': - pos = arg.rfind('*') - if pos == -1: - die('Output argument must be a pointer type') - - outargs.append('static %s out%d;' % (arg[1:pos], i)) - func_args.append(' &out%d' % i) - else: - arg_struct.append(' %s volatile arg%d;' % (arg, i)) - func_args.append('variants[v].in[i].arg%d' % i) - - print(STRUCT_TEMPLATE % {'args' : '\n'.join(arg_struct), 'func': func, - 'func_args': ', '.join(func_args)}) - - # Now print the values. - variants = [] - for (k, vals), i in zip(all_vals.items(), itertools.count()): - out = [' {%s},' % v for v in vals] - - # Members for the variants structure list that we will - # print later. - variants.append(' {"%s(%s)", %d, in%d},' % (func, k, len(vals), i)) - print(ARGS_TEMPLATE % {'argnum': i, 'num_args': len(vals), - 'args': '\n'.join(out)}) - - # Print the variants and the last set of macros. - print(VARIANTS_TEMPLATE % {'num_variants': len(all_vals), - 'variants': '\n'.join(variants)}) - return outargs - - -def _process_directive(d_name, d_val): - """Process a directive. - - Evaluate the directive name and value passed and return the - processed value. This is a helper function for parse_file. - - Args: - d_name: Name of the directive - d_val: The string value to process - - Returns: - The processed value, which may be the string as it is or an object - that describes the directive. - """ - # Process the directive values if necessary. name and ret don't - # need any processing. - if d_name.startswith('include'): - d_val = d_val.split(',') - elif d_name == 'args': - d_val = d_val.split(':') - - # Return the values. - return d_val - - -def parse_file(func): - """Parse an input file - - Given a function name, open and parse an input file for the function - and get the necessary parameters for the generated code and the list - of inputs. - - Args: - func: The function name - - Returns: - A tuple of two elements, one a dictionary of directives and the - other a dictionary of all input values. - """ - all_vals = {} - # Valid directives. - directives = { - 'name': '', - 'args': [], - 'includes': [], - 'include-sources': [], - 'ret': '' - } - - try: - with open('%s-inputs' % func) as f: - for line in f: - # Look for directives and parse it if found. - if line.startswith('##'): - try: - d_name, d_val = line[2:].split(':', 1) - d_name = d_name.strip() - d_val = d_val.strip() - directives[d_name] = _process_directive(d_name, d_val) - except (IndexError, KeyError): - die('Invalid directive: %s' % line[2:]) - - # Skip blank lines and comments. - line = line.split('#', 1)[0].rstrip() - if not line: - continue - - # Otherwise, we're an input. Add to the appropriate - # input set. - cur_name = directives['name'] - all_vals.setdefault(cur_name, []) - all_vals[cur_name].append(line) - except IOError as ex: - die("Failed to open input file (%s): %s" % (ex.filename, ex.strerror)) - - return directives, all_vals - - -def die(msg): - """Exit with an error - - Prints an error message to the standard error stream and exits with - a non-zero status. - - Args: - msg: The error message to print to standard error - """ - print('%s\n' % msg, file=sys.stderr) - sys.exit(os.EX_DATAERR) - - -def main(args): - """Main function - - Use the first command line argument as function name and parse its - input file to generate C source that calls the function repeatedly - for the input. - - Args: - args: The command line arguments with the program name dropped - - Returns: - os.EX_USAGE on error and os.EX_OK on success. - """ - if len(args) != 1: - print('Usage: %s ' % sys.argv[0]) - return os.EX_USAGE - - directives, all_vals = parse_file(args[0]) - gen_source(args[0], directives, all_vals) - return os.EX_OK - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:]))