This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[ping][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: Wed, 11 Dec 2013 12:26:20 +0530
- Subject: [ping][RFC] Implement benchmark script in python
- Authentication-results: sourceware.org; auth=none
- References: <20131206085334 dot GF14845 at spoyarek dot pnq dot redhat dot com>
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])