summaryrefslogtreecommitdiff
path: root/bench
diff options
context:
space:
mode:
authorCraig Silverstein <csilvers@khanacademy.org>2008-04-11 22:36:40 +0000
committerCraig Silverstein <csilvers@khanacademy.org>2008-04-11 22:36:40 +0000
commit596cf4e2f019a766965ad0566495c56b7d295fe4 (patch)
tree1726969de95d86fc5647af1eeb99ea0489ba91c4 /bench
parent0a38eace37b20a663b235fb20107829e22fbeb43 (diff)
downloaddistcc-git-596cf4e2f019a766965ad0566495c56b7d295fe4.tar.gz
The first step of moving everything in the distcc directory to the top
level. I'm doing this in two stages, because I don't understand svn enough to be confident to do it in one. This first stage just copies all the files from distcc/FOO to FOO. Now there are two copies of each file under distcc; the Makefile/etc uses the one in distcc and ignores the one at the top level. The next commit will delete everything under distcc, and rewrite the Makefile/etc to use the top-level versions instead.
Diffstat (limited to 'bench')
-rwxr-xr-xbench/Build.py151
-rwxr-xr-xbench/Project.py114
-rwxr-xr-xbench/ProjectDefs.py114
-rwxr-xr-xbench/Summary.py76
-rwxr-xr-xbench/actions.py51
-rwxr-xr-xbench/benchmark.py176
-rwxr-xr-xbench/buildutil.py49
-rwxr-xr-xbench/compiler.py63
-rwxr-xr-xbench/statistics.py47
9 files changed, 841 insertions, 0 deletions
diff --git a/bench/Build.py b/bench/Build.py
new file mode 100755
index 0000000..67d2817
--- /dev/null
+++ b/bench/Build.py
@@ -0,0 +1,151 @@
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+from Project import Project
+from compiler import CompilerSpec
+import buildutil
+from buildutil import make_dir, run_cmd, rm_files
+import re, os, sys, time
+
+
+
+class Build:
+ """A Build is a combination of a Project and CompilerSpec.
+
+ """
+ def __init__(self, project, compiler, n_repeats):
+ self.project = project
+ self.compiler = compiler
+ self.n_repeats = n_repeats
+
+ self.base_dir = os.path.join(os.getcwd(), "build", self.project.name, self.compiler.name)
+ self.unpacked_dir = os.path.join(self.base_dir, self.project.unpacked_subdir)
+
+ # Some packages need to be started from a subdirectory of their
+ # unpacked form. For example, Samba is compiled from the "source/"
+ # subdirectory of the unpacked source.
+ if self.project.build_subdir:
+ self.build_dir = os.path.join(self.unpacked_dir, project.build_subdir)
+ else:
+ self.build_dir = self.unpacked_dir
+
+ self.log_dir = self.build_dir
+
+ def __repr__(self):
+ return "Build(%s, %s)" % (`self.project`, `self.compiler`)
+
+
+ def unpack(self):
+ """Unpack from source tarball into build directory"""
+ if re.search(r"\.tar\.bz2$", self.project.package_file):
+ tar_fmt = "tar xf %s --bzip2"
+ else:
+ tar_fmt = "tar xfz %s"
+
+ tar_cmd = tar_fmt % os.path.join(os.getcwd(), self.project.package_dir,
+ self.project.package_file)
+
+ make_dir(self.base_dir)
+ print "** Unpacking..."
+ run_cmd("cd %s && %s" % (self.base_dir, tar_cmd))
+
+
+ def configure(self, compiler):
+ """Run configuration command for this tree, if any."""
+ self.compiler = compiler
+
+ make_dir(self.log_dir)
+
+ configure_log = os.path.join(self.log_dir, "bench-configure.log")
+ distcc_log = os.path.join(self.log_dir, "bench-configure-distcc.log")
+
+ rm_files((configure_log, distcc_log))
+
+ print "** Configuring..."
+ run_cmd("cd %s && \\\nDISTCC_LOG='%s' \\\nCC='%s' \\\nCXX='%s' \\\n%s \\\n>%s 2>&1" %
+ (self.build_dir, distcc_log, self.compiler.cc,
+ self.compiler.cxx,
+ self.project.configure_cmd, configure_log))
+
+
+ def build(self, sum):
+ """Actually build the package."""
+
+ build_log = os.path.join(self.log_dir, "bench-build.log")
+ prebuild_log = os.path.join(self.log_dir, "bench-prebuild.log")
+
+ distcc_log = os.path.join(self.log_dir, "bench-build-distcc.log")
+
+ rm_files((build_log, distcc_log))
+
+ print "** Building..."
+ if self.project.pre_build_cmd:
+ cmd = ("cd %s && %s > %s 2>&1" % (self.build_dir,
+ self.project.pre_build_cmd,
+ prebuild_log))
+ run_cmd(cmd)
+
+ cmd = ("cd %s && \\\n%s \\\nDISTCC_LOG='%s' \\\nCC='%s' \\\nCXX='%s' \\\n%s \\\n>%s 2>&1" %
+ (self.build_dir, self.project.build_cmd, distcc_log,
+ self.compiler.cc,
+ self.compiler.cxx,
+ self.compiler.make_opts,
+ build_log))
+ result, elapsed = run_cmd(cmd)
+ return elapsed
+
+
+ def clean(self):
+ clean_log = os.path.join(self.log_dir, "bench-clean.log")
+ print "** Cleaning build directory"
+ cmd = "cd %s && make clean >%s 2>&1" % (self.build_dir, clean_log)
+ run_cmd(cmd)
+
+
+ def scrub(self):
+ print "** Removing build directory"
+ run_cmd("rm -rf %s" % self.unpacked_dir)
+
+
+ def build_actions(self, actions, summary):
+ """Carry out selected actions.
+
+ Catch exceptions and handle."""
+ try:
+ times = []
+ if 'sweep' in actions:
+ self.scrub()
+ if 'unpack' in actions:
+ self.unpack()
+ if 'configure' in actions:
+ self.configure(self.compiler)
+ for i in range(self.n_repeats):
+ if 'build' in actions:
+ times.append(self.build(summary))
+ if 'clean' in actions:
+ self.clean()
+ if 'scrub' in actions:
+ self.scrub()
+ summary.store(self.project, self.compiler, times)
+ except KeyboardInterrupt:
+ raise
+ except:
+ apply(sys.excepthook, sys.exc_info()) # print traceback
+ summary.store(self.project, self.compiler, 'FAIL')
diff --git a/bench/Project.py b/bench/Project.py
new file mode 100755
index 0000000..9551917
--- /dev/null
+++ b/bench/Project.py
@@ -0,0 +1,114 @@
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+import re, os, sys, time
+from buildutil import make_dir, run_cmd
+
+
+# Trees of software to be built.
+trees = { }
+
+
+class Project:
+ """Defines a project to be built and tested.
+
+ The Python process remains in the top-level directory for the
+ whole process. Commands are kicked off in subdirectories if
+ necessary.
+
+ 'subdir' variables give just a single component of a name; 'dir' variables
+ give a full path."""
+
+ def __init__(self, url, package_file=None,
+ md5=None,
+ name=None,
+ configure_cmd=None,
+ pre_build_cmd = None,
+ build_cmd=None,
+ unpacked_subdir=None,
+ build_subdir=None,
+ source_name=None):
+ self.url = url
+ if not package_file:
+ package_file = url.split('/')[-1]
+ self.package_file = package_file
+
+ if not name:
+ name = re.match(r"(.*)\.tar(\.gz|\.bz2|)$", package_file).group(1)
+ self.name = name
+
+ self.md5 = md5
+
+ self.configure_cmd = configure_cmd or "./configure"
+ self.build_cmd = build_cmd or "make"
+ self.source_name = source_name or name
+ self.pre_build_cmd = pre_build_cmd
+
+ self.package_dir = "packages"
+ self.download_dir = "download"
+
+ # By default, we assume the package creates an unpacked
+ # directory whose name is the same as the tarball. For
+ # example, Wine's tarball is "Wine-xxxxxxx", but it unpacks to
+ # "wine-xxxxxxxx".
+ self.unpacked_subdir = unpacked_subdir or self.source_name
+ self.build_subdir = build_subdir
+
+
+ def register(self):
+ trees[self.name] = self
+
+
+ def __repr__(self):
+ return "Project(name=%s)" % `self.name`
+
+
+ def download(self):
+ """Download package from vendor site"""
+
+ make_dir(self.package_dir)
+ make_dir(self.download_dir)
+
+ if not os.path.isfile(os.path.join(self.package_dir, self.package_file)):
+ # XXX: snarf gets upset if the HTTP server returns "416
+ # Requested Range Not Satisfiable" because the file is already
+ # totally downloaded. This is kind of a snarf bug.
+ print "** Downloading"
+ run_cmd("cd %s && wget --continue %s" %
+ (self.download_dir, self.url))
+ run_cmd("mv %s %s" %
+ (os.path.join(self.download_dir, self.package_file),
+ self.package_dir))
+
+ def md5check(self):
+ if self.md5:
+ print "** Checking source package integrity"
+ run_cmd("cd %s && echo '%s' | md5sum -c /dev/stdin" %
+ (self.package_dir, self.md5))
+
+
+ def pre_actions(self, actions):
+ """Perform actions preparatory to building according to selection."""
+
+ if 'download' in actions:
+ self.download()
+ if 'md5check' in actions:
+ self.md5check()
+
diff --git a/bench/ProjectDefs.py b/bench/ProjectDefs.py
new file mode 100755
index 0000000..0d188a8
--- /dev/null
+++ b/bench/ProjectDefs.py
@@ -0,0 +1,114 @@
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003, 2004 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+__doc__ = """distcc benchmark project definitions"""
+
+from Project import Project
+
+# Would like to test glibc, but it needs a separate source and build
+# directory, and this tool doesn't support that yet.
+
+# disable-sanity-checks is needed to stop it wanting linuxthreads --
+# the resulting library is useless, but this is only a test.
+
+#Project(url = 'http://ftp.gnu.org/pub/gnu/glibc/glibc-2.3.2.tar.bz2',
+# configure_cmd = './configure --disable-sanity-checks'
+# ).register()
+
+#Project(url='http://mirror.aarnet.edu.au/pub/gnu/libc/glibc-2.3.tar.bz2',
+# configure_cmd='./configure --disable-sanity-checks',
+# md5='fd20b4a9feeb2b2f0f589b1a9ae8a5e2 glibc-2.3.tar.bz2').register()
+
+Project(url='http://archive.apache.org/dist/httpd/httpd-2.0.43.tar.gz',
+ md5='8051de5d160c43d4ed2cc47dc9be6fd3 httpd-2.0.43.tar.gz').register()
+
+Project(url='ftp://ftp.gtk.org/pub/gtk/v2.0/glib-2.0.7.tar.bz2',
+ md5='5882b1e729f57cb18af653a2f504197b glib-2.0.7.tar.bz2').register()
+
+Project(url='http://us1.samba.org/samba/ftp/old-versions/samba-2.2.7.tar.gz',
+ build_subdir='source',
+ md5='824cd4e305f9b744f3eec702a7b96f7f samba-2.2.7.tar.gz',
+ ).register()
+
+Project(url='http://ftp.gnu.org/gnu/make/make-3.80.tar.bz2',
+ md5='0bbd1df101bc0294d440471e50feca71 *make-3.80.tar.bz2'
+ ).register()
+
+# failed: "make: *** No rule to make target `defconfig'. Stop."
+#Project(url='http://public.ftp.planetmirror.com/pub/linux/kernel/v2.4/linux-2.4.20.tar.bz2',
+# configure_cmd='make defconfig',
+# build_cmd='make bzImage',
+# ).register()
+
+Project(url='http://www.kernel.org/pub/linux/kernel/v2.5/linux-2.5.51.tar.bz2',
+ md5='2300b7b7d2ce4c017fe6dae49717fd9a *linux-2.5.51.tar.bz2',
+ configure_cmd='make defconfig',
+ build_cmd='make bzImage'
+ ).register()
+
+Project(url='http://sources-redhat.oc1.mirrors.redwire.net/gdb/old-releases/gdb-5.3.tar.gz',
+ ).register()
+
+## gimp 1.2.3 has makefile bugs that break -j
+## Project(url='ftp://212.8.35.65/pub/FreeBSD/distfiles/gimp-1.2.3.tar.bz2',
+## md5='b19235f19f524f772a4aef597a69b1da *gimp-1.2.3.tar.bz2',
+## configure_cmd='./configure --disable-perl',
+## ).register()
+
+Project(url='ftp://ftp.gimp.org/pub/gimp/v2.2/gimp-2.2.10.tar.bz2',
+ md5='aa29506ed2272af02941a7a601a7a097 gimp-2.2.10.tar.bz2',
+ configure_cmd='./configure --disable-perl --disable-print',
+ ).register()
+
+## Project(url='http://ibiblio.org/pub/linux/system/emulators/wine/wine-0.9.3.tar.bz2',
+## ).register()
+
+Project(url='http://ibiblio.org/pub/linux/system/emulators/wine/wine-0.9.4.tar.bz2',
+ md5='73205d83a5612a43441a8532683c0434 wine-0.9.4.tar.bz2',
+ ).register()
+
+Project(url='http://public.planetmirror.com.au/pub/gnu/hello/hello-2.1.1.tar.gz',
+ md5='70c9ccf9fac07f762c24f2df2290784d *hello-2.1.1.tar.gz',
+ ).register()
+
+
+# XXX: Does not build on Debian at the moment, problem with libIDL-config
+
+# Project(url='http://mirror.aarnet.edu.au/pub/mozilla/releases/mozilla1.4/src/mozilla-source-1.4.tar.bz2',
+# name='mozilla-1.4',
+# configure_cmd="LIBIDL_CONFIG=libIDL-config-2 ./configure",
+# unpacked_subdir='mozilla',
+# ).register()
+
+
+Project(url='http://ftp.mozilla.org/pub/firebird/releases/0.6/MozillaFirebird-0.6-source.tar.bz2',
+ name='MozillaFirebird',
+ unpacked_subdir='mozilla',
+ ).register()
+
+Project(url='http://us1.samba.org/samba/ftp/old-versions/samba-3.0.20.tar.gz',
+ name='samba-3.0.20',
+ build_subdir='source',
+ # newer versions of popt can be incompatible
+ configure_cmd='./configure --with-included-popt',
+ pre_build_cmd = 'make proto',
+ ).register()
+
+
diff --git a/bench/Summary.py b/bench/Summary.py
new file mode 100755
index 0000000..33c8111
--- /dev/null
+++ b/bench/Summary.py
@@ -0,0 +1,76 @@
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+class Summary:
+ """Stores and prints results of building different things"""
+
+ # Table is a sequence, because we prefer to have things printed
+ # out in the order they were executed.
+
+ def __init__(self):
+ self._table = []
+
+ def store(self, project, compiler, elapsed_times):
+ """
+ elapsed_times is a sequence of elapsed times to build the project.
+ A sequence because we can build projects repeatedly.
+ """
+ self._table.append((project.name, compiler.name, elapsed_times))
+
+ def print_raw(self):
+ from pprint import pprint
+ pprint(self._table)
+
+ def print_table(self):
+ import time, os, sys
+ import statistics
+
+ # if nothing was run, skip it
+ if not len(self._table):
+ return
+
+ """Print out in a nice tabular form"""
+ print """
+ ========================
+ distcc benchmark results
+ ========================
+
+"""
+ print "Date: ", time.ctime()
+ print "DISTCC_HOSTS: %s" % `os.getenv('DISTCC_HOSTS')`
+ sys.stdout.flush()
+ os.system("uname -a")
+
+ print "%-20s %-30s %8s %8s" % ('project', 'compiler', 'time', 's.d.')
+
+ for row in self._table:
+ print "%-20s %-30s " % row[:2],
+ times = row[2]
+ if times == 'FAIL':
+ print '%9s' % 'FAIL'
+ else:
+ mean = statistics.mean(times)
+ sd = statistics.std(times)
+ print "%8.4fs" % mean,
+ if sd is None:
+ print "%9s" % "n/a"
+ else:
+ print "%8.4fs" % sd
+
diff --git a/bench/actions.py b/bench/actions.py
new file mode 100755
index 0000000..d6c3544
--- /dev/null
+++ b/bench/actions.py
@@ -0,0 +1,51 @@
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+# Tuples of (name, default, descr)
+all_actions = [('download', True, ''),
+ ('md5check', True, 'check file was downloaded correctly'),
+ ('sweep', True, 'remove build directory before unpacking'),
+ ('unpack', True, 'unpack source'),
+ ('configure', True, ''),
+ ('clean', True, 'run "make clean" or equivalent'),
+ ('build', True, ''),
+ ('scrub', True, 'remove build directory')]
+
+
+def action_help():
+ print "Actions:"
+ for action, default, descr in all_actions:
+ default_ch = default and '*' or ' '
+ print " %c %-20s %s" % (default_ch, action, descr)
+ print " (* = on by default)"
+
+
+# Filter out only actions where 'default' is true
+default_actions = [a[0] for a in all_actions if a[1]]
+
+
+def parse_opt_actions(optarg):
+ import sys
+ opt_actions = optarg.split(',')
+ action_names = [a[0] for a in all_actions]
+ for oa in opt_actions:
+ if oa not in action_names:
+ raise ValueError, ("no such action: %s" % `oa`)
+ return opt_actions
diff --git a/bench/benchmark.py b/bench/benchmark.py
new file mode 100755
index 0000000..a4f80b3
--- /dev/null
+++ b/bench/benchmark.py
@@ -0,0 +1,176 @@
+#! /usr/bin/python
+
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003, 2004 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+
+# Unlike the main distcc test suite, this program *does* require you
+# to manually set up servers on your choice of machines on the
+# network, and make sure that they all have appropriate compilers
+# installed. The performance measurements obviously depend on the
+# network and hardware available.
+
+# It also depends on you having the necessary dependencies to build
+# the software. If you regularly build software on Linux you should
+# be OK. Some things (the GIMP) will be harder than others.
+
+# On some platforms, it may be impossible to build some targets -- for
+# example, Linux depends on having a real-mode x86 assembler, which
+# probably isn't installed on Solaris.
+
+# Note that running this program will potentially download many
+# megabytes of test data.
+
+
+# TODO: Support applying patches after unpacking, before building.
+# For example, they might be needed to fix -j bugs in the Makefile.
+
+# TODO: In stats, show ratio of build time to slowest build time. (Or
+# to first one run?)
+
+# TODO: Allow choice of which compiler and make options to use.
+
+# TODO: Try building something large in C++.
+
+# TODO: Set CXX as well.
+
+# TODO: Add option to run tests repeatedly and show mean and std. dev.
+
+# TODO: Perhaps add option to do "make clean" -- this might be faster
+# than unzipping and configuring every time. But perhaps also less
+# reproducible.
+
+# TODO: Add option to run tests on different sets or orderings of
+# machines.
+
+
+import re, os, sys, time
+from getopt import getopt
+
+from Summary import Summary
+from Project import Project, trees
+from compiler import CompilerSpec
+from Build import Build
+import actions, compiler
+
+import ProjectDefs # this adds a lot of definitions to 'trees'
+
+
+def error(msg):
+ sys.stderr.write(msg + "\n")
+
+
+def list_projects():
+ names = trees.keys()
+ names.sort()
+ for n in names:
+ print n
+
+
+def find_project(name):
+ """
+ Return the nearest unique match for name.
+ """
+ best_match = None
+ for pn in trees.keys():
+ if pn.startswith(name):
+ if best_match:
+ raise ValueError, "ambiguous prefix %s" % name
+ else:
+ best_match = pn
+
+ if not best_match:
+ raise ValueError, "nothing matches %s" % name
+ else:
+ return trees[best_match]
+
+
+
+def show_help():
+ print """Usage: benchmark.py [OPTION]... [PROJECT]...
+Test distcc relative performance building different projects.
+By default, all known projects are built.
+
+Options:
+ --help show brief help message
+ --list-projects show defined projects
+ -c, --compiler=COMPILER specify one compiler to use
+ -n N repeat compilation N times
+ -a, --actions=ACTIONS comma-separated list of action phases
+ to perform
+
+Compilers can be specified as either "local,N" to run N copies of gcc,
+or dist,N to run N copies of distcc. Multiple -c options specify
+different scenarios to measure. The default is to run a nonparallel
+local compile and a parallel distributed compile.
+"""
+actions.action_help()
+
+
+# -a is for developer use only and not documented; unless you're
+# careful the results will just be confusing.
+
+
+
+
+######################################################################
+def main():
+ """Run the benchmark per arguments"""
+ sum = Summary()
+ options, args = getopt(sys.argv[1:], 'a:c:n:',
+ ['list-projects', 'actions=', 'help', 'compiler='])
+ opt_actions = actions.default_actions
+ set_compilers = []
+ opt_repeats = 1
+
+ for opt, optarg in options:
+ if opt == '--help':
+ show_help()
+ return
+ elif opt == '--list-projects':
+ list_projects()
+ return
+ elif opt == '--actions' or opt == '-a':
+ opt_actions = actions.parse_opt_actions(optarg)
+ elif opt == '--compiler' or opt == '-c':
+ set_compilers.append(compiler.parse_opt(optarg))
+ elif opt == '-n':
+ opt_repeats = int(optarg)
+
+ if not set_compilers:
+ set_compilers = compiler.default_compilers()
+
+ # Find named projects, or run all by default
+ if args:
+ chosen_projects = [find_project(name) for name in args]
+ else:
+ chosen_projects = trees.values()
+
+ for proj in chosen_projects:
+ proj.pre_actions(opt_actions)
+ for comp in set_compilers:
+ build = Build(proj, comp, opt_repeats)
+ build.build_actions(opt_actions, sum)
+
+ sum.print_table()
+
+if __name__ == '__main__':
+ main()
+
diff --git a/bench/buildutil.py b/bench/buildutil.py
new file mode 100755
index 0000000..8a43353
--- /dev/null
+++ b/bench/buildutil.py
@@ -0,0 +1,49 @@
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+
+def make_dir(d):
+ import os
+ if not os.path.isdir(d):
+ os.makedirs(d)
+
+
+def run_cmd(cmd, expected=0):
+ import time, os
+
+ before = time.time()
+ print '%% %s' % cmd
+ result = os.system(cmd)
+ after = time.time()
+ elapsed = (after - before)
+ print '%16.4fs elapsed\n' % elapsed
+ if expected is not None:
+ if expected != result:
+ raise AssertionError("command failed: expected status %d, got %d",
+ expected, result)
+ return result, elapsed
+
+
+def rm_files(file_list):
+ import os
+ for f in file_list:
+ if os.path.exists(f):
+ os.unlink(f)
+
diff --git a/bench/compiler.py b/bench/compiler.py
new file mode 100755
index 0000000..3cc9d1d
--- /dev/null
+++ b/bench/compiler.py
@@ -0,0 +1,63 @@
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2002, 2003 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+class CompilerSpec:
+ """Describes a compiler/make setup.
+
+ Used to define different situations such as local compilation, and
+ various degrees of parallelism."""
+
+ def __init__(self, cc=None, cxx=None, make_opts=None, name=None):
+ self.cc = cc or 'gcc'
+ self.cxx = cxx or 'c++'
+ self.make_opts = make_opts or ''
+ self.name = name or (self.cc + "__" + self.make_opts).replace(' ', '_')
+
+
+def default_compilers():
+ return [parse_opt('local,1'),
+ parse_opt('dist,8'),
+ ]
+
+def parse_opt(optarg):
+ """Parse command-line specification of a compiler
+
+ XXX: I don't really know what the best syntax for this is. For
+ the moment, it is "local" or "dist", followed by a comma and a
+ -j number. Perhaps we need to be able to specify host lists here
+ too.
+ """
+ where, howmany = optarg.split(',')
+ howmany = int(howmany)
+ if where == 'local':
+ return CompilerSpec(name='local_%02d' % howmany,
+ cc='cc',
+ cxx='c++',
+ make_opts='-j%d' % howmany)
+ elif where == 'dist':
+ return CompilerSpec(name='dist_%02d' % howmany,
+ cxx='distcc c++',
+ cc='distcc cc',
+ make_opts='-j%d' % howmany)
+ else:
+ raise ValueError, ("don't understand %s" % `where`)
+
+
+
diff --git a/bench/statistics.py b/bench/statistics.py
new file mode 100755
index 0000000..1565752
--- /dev/null
+++ b/bench/statistics.py
@@ -0,0 +1,47 @@
+#! /usr/bin/env python2.2
+
+# distcc/benchmark -- automated system for testing distcc correctness
+# and performance on various source trees.
+
+# Copyright (C) 2003 by Martin Pool
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# This program 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
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+
+# Based in part on http://starship.python.net/crew/jhauser/NumAdd.py.html by Janko Hauser
+
+
+import Numeric
+
+def var(m):
+ """
+ Variance of m.
+ """
+ if len(m) < 2:
+ return None
+ mu = Numeric.average(m)
+ return (Numeric.add.reduce(Numeric.power(Numeric.ravel(m)-mu, 2))
+ / (len(m)-1.))
+
+def std(m):
+ """
+ Standard deviation of m.
+ """
+ v = var(m)
+ return v and Numeric.sqrt(v)
+
+def mean(m):
+ return Numeric.average(m)