This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[ping2][RFC] Implement benchmark script in python
- From: Siddhesh Poyarekar <siddhesh at redhat dot com>
- To: libc-alpha at sourceware dot org
- Cc: carlos at redhat dot com
- Date: Thu, 19 Dec 2013 15:03:32 +0530
- Subject: [ping2][RFC] Implement benchmark script in python
- Authentication-results: sourceware.org; auth=none
- References: <20131206085334 dot GF14845 at spoyarek dot pnq dot redhat dot com> <20131211065620 dot GD20835 at spoyarek dot pnq dot redhat dot com>
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])