diff options
Diffstat (limited to 'benchmarks/CUDA/CP/driver/benchmark.py')
| -rw-r--r-- | benchmarks/CUDA/CP/driver/benchmark.py | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/benchmarks/CUDA/CP/driver/benchmark.py b/benchmarks/CUDA/CP/driver/benchmark.py new file mode 100644 index 0000000..43d7852 --- /dev/null +++ b/benchmarks/CUDA/CP/driver/benchmark.py @@ -0,0 +1,374 @@ +# (c) 2007 The Board of Trustees of the University of Illinois. + +import sys +import os +from os import path +import re +from itertools import imap, repeat, chain + +import globals +import process +from futures import Future + +class Benchmark(object): + """A benchmark. + + If the benchmark is malformed or otherwise invalid, only the 'name' and + 'invalid' fields will be set. Otherwise all fields will be set. + + Fields: + name The name of the benchmark. This is also the benchmark + directory name. + invalid None if the benchmark is valid; otherwise, an exception + describing why the benchmark is invalid. + path Full path of the benchmark directory. + descr A description of the benchmark. + impls A dictionary of benchmark source implementations. + datas A dictionary of data sets used to run the benchmark.""" + + def __init__(self, name, path = None, impls = [], datasets = [], + description=None, invalid=None): + self.name = name + self.invalid = invalid + + if invalid is None: + self.path = path + self.impls = dict(imap(lambda i: (i.name, i), impls)) + self.datas = dict(imap(lambda i: (i.name, i), datasets)) + self.descr = description + + def createFromName(name): + """Scan the benchmark directory for the benchmark named 'name' + and create a benchmark object for it.""" + bmkpath = path.join(globals.root, 'benchmarks', name) + descr = process.read_description_file(bmkpath) + + try: + # Scan implementations of the benchmark + impls = [BenchImpl.createFromName(name, impl) + for impl in process.scan_for_benchmark_versions(bmkpath)] + + # Scan data sets of the benchmark + datas = [BenchDataset.createFromName(name, data) + for data in process.scan_for_benchmark_datasets(bmkpath)] + + # If no exception occurred, the benchmark is valid + return Benchmark(name, bmkpath, impls, datas, descr) + except Exception, e: + return Benchmark(name, invalid=e) + + createFromName = staticmethod(createFromName) + + def describe(self): + """Return a string describing this benchmark.""" + + if self.invalid: + return "Error in benchmark:\n" + str(self.invalid) + + if self.descr is None: + header = "Benchmark '" + self.name + "'" + else: + header = self.descr + + impls = " ".join([impl.name for impl in self.impls.itervalues()]) + datas = " ".join([data.name for data in self.datas.itervalues()]) + + return header + "\nVersions: " + impls + "\nData sets: " + datas + + def instance_check(x): + if not isinstance(x, Benchmark): + raise TypeError, "argument must be an instance of Benchmark" + + instance_check = staticmethod(instance_check) + +class BenchImpl(object): + """An implementation of a benchmark.""" + + def __init__(self, name, description=None): + if not isinstance(name, str): + raise TypeError, "name must be a string" + + self.name = name + self.descr = description + + def createFromName(name, impl): + """Scan the directory containing a benchmark implementation + and create a BenchImpl object from it.""" + + # Path to the implementation + impl_path = path.join(globals.root, 'benchmarks', name, 'src', impl) + + # Get the description from a file, if provided + descr = process.read_description_file(impl_path) + + return BenchImpl(impl, descr) + + createFromName = staticmethod(createFromName) + + def makefile(self, benchmark, target=None, action=None): + """Run this implementation's makefile.""" + + Benchmark.instance_check(benchmark) + + def perform(): + srcdir = path.join('src', self.name) + builddir = path.join('build', self.name) + + env={'SRCDIR':srcdir, + 'BUILDDIR':builddir, + 'BIN':path.join(builddir,benchmark.name), + 'PARBOIL_ROOT':globals.root} + + # Run the makefile to build the benchmark + return process.makefile(target=target, + action=action, + filepath=path.join(srcdir, "Makefile"), + env=env) + + # Go to the benchmark directory before building + return process.with_path(benchmark.path, perform) + + def build(self, benchmark): + """Build an executable of this benchmark implementation.""" + return self.makefile(benchmark) + + def isBuilt(self, benchmark): + """Determine whether the executable is up to date.""" + return self.makefile(benchmark, action='q') + + def clean(self, benchmark): + """Remove build files for this benchmark implementation.""" + return self.makefile(benchmark, 'clean') + + def run(self, benchmark, dataset, do_output=True, extra_opts=[]): + """Run this benchmark implementation. + + Return True if the benchmark terminated normally or False + if there was an error.""" + + # Ensure that the benchmark has been built + if not self.isBuilt(benchmark): + rc = self.build(benchmark) + + # Stop if 'make' failed + if not rc: return False + + def perform(): + # Run the program + exename = path.join('build', self.name, benchmark.name) + args = [exename] + extra_opts + dataset.getCommandLineArguments(do_output) + rc = process.spawnwaitv(exename, args) + + # Program exited with error? + if rc != 0: return False + return True + + return process.with_path(benchmark.path, perform) + + def check(self, benchmark, dataset): + """Check the output from the last run of this benchmark + implementation. + + Return True if the output checks successfully or False + otherwise.""" + + def perform(): + output_file = dataset.getTemporaryOutputPath() + reference_file = dataset.getReferenceOutputPath() + + compare = os.path.join('tools', 'compare-output') + rc = process.spawnwaitl(compare, + compare, reference_file, output_file) + + # Program exited with error, or mismatch in output? + if rc != 0: return False + return True + + return process.with_path(benchmark.path, perform) + + def __str__(self): + return "<BenchImpl '" + self.name + "'>" + +class BenchDataset(object): + """Data sets for running a benchmark.""" + + def __init__(self, name, in_files=[], out_files=[], parameters=[], + description=None): + if not isinstance(name, str): + raise TypeError, "name must be a string" + + self.name = name + self.inFiles = in_files + self.outFiles = out_files + self.parameters = parameters + self.descr = description + + def createFromName(name, dset): + """Scan the directory containing a dataset + and create a BenchDataset object from it.""" + + # Identify the paths where files may be found + benchmark_path = path.join(globals.root, 'benchmarks', name) + + if path.exists(path.join(benchmark_path, 'input')): + input_path = path.join(benchmark_path, 'input', dset) + else: + input_path = None + + output_path = path.join(benchmark_path, 'output', dset) + + # Look for input files + + def check_default_input_files(): + # This function is called to see if the input file set + # guessed by scanning the input directory can be used + if invalid_default_input_files: + raise ValueError, "Cannot infer command line when there are multiple input files in a data set\n(Fix by adding an input DESCRIPTION file)" + + if input_path: + input_descr = process.read_description_file(input_path) + input_files = list(process.scan_for_files(input_path, + boring=['DESCRIPTION','.svn'])) + + # If more than one input file was found, cannot use the default + # input file list produced by scanning the directory + invalid_default_input_files = len(input_files) > 1 + else: + # If there's no input directory, assume the benchmark + # takes no input + input_descr = None + input_files = [] + invalid_default_input_files = False + + # Read the text of the input description file + if input_descr is not None: + (parameters, input_files1, input_descr) = \ + unpack_dataset_description(input_descr, input_files=None) + + if input_files1 is None: + # No override vaule given; use the default + check_default_input_files() + else: + input_files = input_files1 + else: + check_default_input_files() + parameters = [] + + # Look for output files + output_descr = process.read_description_file(output_path) + output_files = list(process.scan_for_files(output_path, + boring=['DESCRIPTION','.svn'])) + if len(output_files) > 1: + raise ValueError, "Multiple output files not supported" + + # Concatenate input and output descriptions + if input_descr and output_descr: + descr = input_descr + "\n\n" + output_descr + else: + descr = input_descr or output_descr + + return BenchDataset(dset, input_files, output_files, parameters, descr) + + createFromName = staticmethod(createFromName) + + def getTemporaryOutputPath(self): + """Get the name of a file used to hold the output of a benchmark run. + This function should always return the same name if its parameters + are the same. The output path is not the path where the reference + output is stored.""" + + return path.join('run', self.name, self.outFiles[0]) + + def getReferenceOutputPath(self): + """Get the name of the reference file, to which the output of a + benchmark run should be compared.""" + + return path.join('output', self.name, self.outFiles[0]) + + def getCommandLineArguments(self, do_output=True): + """Get the command line arguments that should be passed to the + executable to run this data set. If 'output' is True, then + the executable will be passed flags to save its output to a file. + + Directories to hold ouptut files are created if they do not exist.""" + args = [] + + # Add arguments to pass input files to the benchmark + if self.inFiles: + in_files = ",".join([path.join('input', self.name, x) + for x in self.inFiles]) + args.append("-i") + args.append(in_files) + + # Add arguments to store the output somewhere, if output is + # desired + if do_output and self.outFiles: + if len(self.outFiles) != 1: + raise ValueError, "only one output file is supported" + + out_path = self.getTemporaryOutputPath() + args.append("-o") + args.append(out_path) + + # Ensure that a directory exists for the output + process.touch_directory(path.dirname(out_path)) + + args += self.parameters + return args + + def __str__(self): + return "<BenchData '" + self.name + "'>" + +def unpack_dataset_description(descr, parameters=[], input_files=[]): + """Read information from the raw contents of a data set description + file. Optional 'parameters' and 'input_files' arguments may be + given, which will be retained unless overridden by the description + file.""" + leftover = [] + split_at_colon = re.compile(r"^\s*([a-zA-Z]+)\s*:(.*)$") + + # Initialize these to default empty strings + parameter_text = None + input_file_text = None + + # Scan the description line by line + for line in descr.split('\n'): + m = split_at_colon.match(line) + if m is None: continue + + # This line appears to declare something that should be + # interpreted + keyword = m.group(1) + if keyword == "Parameters": + parameter_text = m.group(2) + elif keyword == "Inputs": + input_file_text = m.group(2) + # else, ignore the line + + # Split the strings into (possibly) multiple arguments, discarding + # whitespace + if parameter_text is not None: parameters = parameter_text.split() + if input_file_text is not None: input_files = input_file_text.split() + return (parameters, input_files, descr) + +def find_benchmarks(): + """Find benchmarks in the repository. The benchmarks are + identified, but their contents are not scanned immediately. A + dictionary is returned mapping benchmark names to futures + containing the benchmarks.""" + + if not globals.root: + raise ValueError, "root directory has not been set" + + # Scan all benchmarks in the 'benchmarks' directory and + # lazily create benchmark objects. + db = {} + try: + for bmkname in process.scan_for_benchmarks(globals.root): + bmk = Future(lambda bmkname=bmkname: Benchmark.createFromName(bmkname)) + db[bmkname] = bmk + except OSError, e: + sys.stdout.write("Benchmark directory not found!\n\n") + return {} + + return db |
