This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH][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: Fri, 6 Dec 2013 14:23:34 +0530
- Subject: [PATCH][RFC] Implement benchmark script in python
- Authentication-results: sourceware.org; auth=none
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])