This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[ping2][RFC] Implement benchmark script in python


Ping!

On Wed, Dec 11, 2013 at 12:26:20PM +0530, Siddhesh Poyarekar wrote:
> Ping!
> 
> On Fri, Dec 06, 2013 at 02:23:34PM +0530, Siddhesh Poyarekar wrote:
> > Hi,
> > 
> > I reimplemented the benchmark code generation script in python since I
> > figured it would be nicer to read and maintain.  This has the
> > disadvantage of an added dependency on python - I think someone (or
> > maybe me?) mentioned it as a problem in the past - but it could be an
> > optional dependency only for those who want to run benchmarks.  I was
> > also emboldened by Will's (really old) patch to add a python script
> > that generates graphs for benchmark outputs.
> > 
> > I have posted the script below, which has been tested to verify that
> > it generates identical code barring whitespace differences and removal
> > of some extra semi-colons.  All current benchmarks build correctly.
> > Comments?
> > 
> > Siddhesh
> > 
> > 
> > #!/usr/bin/env python
> > # Copyright (C) 2013 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
> > # <http://www.gnu.org/licenses/>.
> > 
> > import sys
> > 
> > all_vals = {}
> > # Valid directives.
> > directives = {"name": "",
> > 	      "args": [],
> > 	      "includes": [],
> > 	      "include-sources": [],
> > 	      "ret": ""}
> > 
> > # Generate the C source for the function from the values and directives.
> > def gen_source(func):
> >     for h in directives["includes"]:
> > 	print "#include <%s>" % h
> > 
> >     for h in directives["include-sources"]:
> > 	print "#include \"%s\"" % h
> > 
> >     # Print macros.  This branches out to a separate routine if
> >     # the function takes arguments.
> >     if not directives["args"]:
> > 	print "#define CALL_BENCH_FUNC(v, i) %s ()" % func
> > 	print "#define NUM_VARIANTS (1)"
> > 	print "#define NUM_SAMPLES(v) (1)"
> > 	print "#define VARIANT(v) FUNCNAME \"()\""
> > 	outargs = []
> >     else:
> > 	outargs = _print_arg_data (func)
> > 
> >     # 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 "#define BENCH_FUNC(i, j) ({%s CALL_BENCH_FUNC (i, j);})" % getret
> >     print "#define FUNCNAME \"%s\"" % func
> >     print "#include \"bench-skeleton.c\""
> > 
> > 
> > # Print structure and values for arguments and their variants and return output
> > # arguments if any are found.
> > def _print_arg_data(func):
> >     # 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.
> >     n = 0
> >     func_args = []
> >     arg_struct = []
> >     outargs = []
> > 
> >     for arg in directives["args"]:
> > 	if arg[0] == '<' and arg[-1] == '>':
> > 	    pos = arg.rfind('*')
> > 	    if pos == -1:
> > 		print >> sys.stderr, "Output argument must be a pointer type"
> > 		sys.exit(1)
> > 
> > 	    outargs.append("static %s out%d;" % (arg[1:pos], n))
> > 	    func_args.append(" &out%d" % n)
> > 	else:
> > 	    arg_struct.append("%s volatile arg%d;" % (arg, n))
> > 	    func_args.append("variants[v].in[i].arg%d" % n)
> > 	n = n + 1
> > 
> >     func_args = ','.join(func_args)
> >     print "#define CALL_BENCH_FUNC(v, i) %s (%s)" % (func, func_args)
> >     print "struct args {%s};" % '\n'.join(arg_struct)
> > 
> >     print "struct _variants"
> >     print "{"
> >     print "  const char *name;"
> >     print "  int count;"
> >     print "  struct args *in;"
> >     print "};"
> > 
> >     # Now print the values.
> >     n = 0
> >     variants = []
> >     for k in all_vals.keys():
> > 	vals = all_vals[k]
> > 	out = map (lambda v: "{%s}," % v, vals)
> > 
> > 	# Members for the variants structure list that we will
> > 	# print later.
> > 	variants.append("{\"%s(%s)\", %d, in%d}," % (func, k, len(vals), n))
> > 
> > 	print "struct args in%d[%d] = {" % (n, len(vals))
> > 	print '\n'.join(out)
> > 	print "};\n"
> > 	n = n + 1
> > 
> >     print "struct _variants variants[%d] = {" % len(all_vals)
> >     print '\n'.join(variants)
> >     print "};\n"
> > 
> >     # Finally, print the last set of macros.
> >     print "#define NUM_VARIANTS %d" % len(all_vals)
> >     print "#define NUM_SAMPLES(i) (variants[i].count)\n"
> >     print "#define VARIANT(i) (variants[i].name)\n"
> >     return outargs
> > 
> > 
> > # Parse a directive.
> > def _parse_directive(d):
> >     global directives
> > 
> >     try:
> > 	d_name = d[0].strip()
> > 	d_val = d[1].strip()
> > 	# Make sure that the key exists.  We could use the 'in' semantics, but
> > 	# this is just simpler.
> > 	directives[d_name]
> >     except (IndexError, KeyError):
> > 	print >> sys.stderr, "Invalid directive:", ':'.join(d)
> > 	sys.exit(1)
> > 
> >     # 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(':')
> > 
> >     # Add the values.
> >     directives[d_name] = d_val
> > 
> > 
> > def parse_file(func):
> >     global all_vals
> >     try:
> > 	file_handle = open(func + "-inputs", "r")
> >     except IOError as e:
> > 	print >> sys.stderr, "Could not open benchmark input file:", e.strerror
> > 	sys.exit(1)
> > 
> >     for line in file_handle:
> > 	line = line.strip()
> > 
> > 	# Look for directives and parse it if found.
> > 	if line.startswith("##"):
> > 	    _parse_directive(line[2:].split(':', 1))
> > 
> > 	# Skip blank lines and comments.
> > 	if not line or line[0] == '#':
> > 	    continue
> > 
> > 	# Otherwise, we're an input.  Add to the appropriate input set.
> > 	cur_name = directives["name"]
> > 	try:
> > 	    all_vals[cur_name].append(line)
> > 	except KeyError:
> > 	    all_vals[cur_name] = []
> > 	    all_vals[cur_name].append(line)
> > 
> > 
> > if __name__ == '__main__':
> >     if not sys.argv[1:]:
> > 	print "Usage: ", sys.argv[0], " <function>"
> > 	sys.exit(1)
> > 
> >     parse_file(sys.argv[1])
> >     gen_source(sys.argv[1])


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]