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]

[PATCH][RFC] Implement benchmark script in python


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]