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]

[ping][RFC] Implement benchmark script in python


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]