--- /dev/null
+#! /usr/bin/env python
+#
+# Copyright (C) 2010 Joel Rosdahl
+#
+# 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 3 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., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from optparse import OptionParser
+from os import access, environ, mkdir, getpid, X_OK
+from os.path import (
+ abspath, basename, exists, isabs, isfile, join as joinpath, realpath,
+ splitext)
+from shutil import rmtree
+from subprocess import call
+from time import time
+import sys
+
+USAGE = """%prog [options] <compiler> [compiler options] <source code file>"""
+
+DESCRIPTION = """\
+This program compiles a C/C++ file with/without ccache a number of times to get
+some idea of ccache speedup and overhead in the preprocessor and direct modes.
+The arguments to the program should be the compiler, optionally followed by
+compiler options, and finally the source file to compile. The compiler options
+must not contain -c or -o as these options will be added later. Example:
+./perf.py gcc -g -O2 -Idir file.c
+"""
+
+DEFAULT_CCACHE = "./ccache"
+DEFAULT_DIRECTORY = "."
+DEFAULT_TIMES = 30
+
+PHASES = [
+ "without ccache",
+ "with ccache, preprocessor mode, cache miss",
+ "with ccache, preprocessor mode, cache hit",
+ "with ccache, direct mode, cache miss",
+ "with ccache, direct mode, cache hit"]
+
+verbose = False
+
+def progress(msg):
+ if verbose:
+ sys.stdout.write(msg)
+ sys.stdout.flush()
+
+def recreate_dir(x):
+ if exists(x):
+ rmtree(x)
+ mkdir(x)
+
+def test(tmp_dir, options, compiler_args, source_file):
+ src_dir = "%s/src" % tmp_dir
+ obj_dir = "%s/obj" % tmp_dir
+ ccache_dir = "%s/ccache" % tmp_dir
+ mkdir(src_dir)
+ mkdir(obj_dir)
+
+ compiler_args += ["-c", "-o"]
+ extension = splitext(source_file)[1]
+ times = options.times
+
+ progress("Creating source code\n")
+ for i in range(times):
+ fp = open("%s/%d%s" % (src_dir, i, extension), "w")
+ fp.write(open(source_file).read())
+ fp.write("\nint ccache_perf_test_%d;\n" % i)
+ fp.close()
+
+ environment = {"CCACHE_DIR": ccache_dir, "PATH": environ["PATH"]}
+ if options.compression:
+ environment["CCACHE_COMPRESS"] = "1"
+ if options.hardlink:
+ environment["CCACHE_HARDLINK"] = "1"
+
+ result = [None] * len(PHASES)
+
+ def run(i, use_ccache, use_direct):
+ obj = "%s/%d.o" % (obj_dir, i)
+ src = "%s/%d%s" % (src_dir, i, extension)
+ if use_ccache:
+ args = [options.ccache]
+ else:
+ args = []
+ args += compiler_args + [obj, src]
+ env = environment.copy()
+ if not use_direct:
+ env["CCACHE_NODIRECT"] = "1"
+ if call(args, env=env) != 0:
+ sys.stderr.write(
+ 'Error running "%s"; please correct\n' % " ".join(args))
+ sys.exit(1)
+
+ # Warm up the disk cache.
+ recreate_dir(ccache_dir)
+ recreate_dir(obj_dir)
+ run(0, True, True)
+
+ ###########################################################################
+ # Without ccache
+ recreate_dir(ccache_dir)
+ recreate_dir(obj_dir)
+ progress("Compiling %s\n" % PHASES[0])
+ t0 = time()
+ for i in range(times):
+ run(i, False, False)
+ progress(".")
+ result[0] = time() - t0
+ progress("\n")
+
+ ###########################################################################
+ # Preprocessor mode
+ recreate_dir(ccache_dir)
+ recreate_dir(obj_dir)
+ progress("Compiling %s\n" % PHASES[1])
+ t0 = time()
+ for i in range(times):
+ run(i, True, False)
+ progress(".")
+ result[1] = time() - t0
+ progress("\n")
+
+ recreate_dir(obj_dir)
+ progress("Compiling %s\n" % PHASES[2])
+ t0 = time()
+ for i in range(times):
+ run(i, True, False)
+ progress(".")
+ result[2] = time() - t0
+ progress("\n")
+
+ ###########################################################################
+ # Direct mode
+ recreate_dir(ccache_dir)
+ recreate_dir(obj_dir)
+ progress("Compiling %s\n" % PHASES[3])
+ t0 = time()
+ for i in range(times):
+ run(i, True, True)
+ progress(".")
+ result[3] = time() - t0
+ progress("\n")
+
+ recreate_dir(obj_dir)
+ progress("Compiling %s\n" % PHASES[4])
+ t0 = time()
+ for i in range(times):
+ run(i, True, True)
+ progress(".")
+ result[4] = time() - t0
+ progress("\n")
+
+ return result
+
+def print_result(result):
+ for (i, x) in enumerate(PHASES):
+ print "%-43s %6.2f s (%6.2f %%) (%5.2f x)" % (
+ x.capitalize() + ":",
+ result[i],
+ 100 * (result[i] / result[0]),
+ result[0] / result[i])
+
+def on_off(x):
+ return "on" if x else "off"
+
+def find_in_path(cmd):
+ if isabs(cmd):
+ return cmd
+ else:
+ for path in environ["PATH"].split(":"):
+ p = joinpath(path, cmd)
+ if isfile(p) and access(p, X_OK):
+ return p
+ return None
+
+def main(argv):
+ op = OptionParser(usage=USAGE, description=DESCRIPTION)
+ op.disable_interspersed_args()
+ op.add_option(
+ "--ccache",
+ help="location of ccache (default: %s)" % DEFAULT_CCACHE)
+ op.add_option(
+ "--compression",
+ help="use compression",
+ action="store_true")
+ op.add_option(
+ "-d", "--directory",
+ help="where to create the temporary directory with the cache and" \
+ " other files (default: %s)" \
+ % DEFAULT_DIRECTORY)
+ op.add_option(
+ "--hardlink",
+ help="use hard links",
+ action="store_true")
+ op.add_option(
+ "-n", "--times",
+ help="number of times to compile the file (default: %d)" \
+ % DEFAULT_TIMES,
+ type="int")
+ op.add_option(
+ "-v", "--verbose",
+ help="print progress messages",
+ action="store_true")
+ op.set_defaults(
+ ccache=DEFAULT_CCACHE,
+ directory=DEFAULT_DIRECTORY,
+ times=DEFAULT_TIMES)
+ (options, args) = op.parse_args(argv[1:])
+ if len(args) < 2:
+ op.error("Missing arguments; pass -h/--help for help")
+
+ global verbose
+ verbose = options.verbose
+
+ options.ccache = abspath(options.ccache)
+
+ compiler = find_in_path(args[0])
+ if compiler is None:
+ op.error("Could not find %s in PATH" % args[0])
+ if "ccache" in basename(realpath(compiler)):
+ op.error(
+ "%s seems to be a symlink to ccache; please specify the path to"
+ " the real compiler instead" % compiler)
+
+ print "Compilation command: %s -c -o %s.o" % (
+ " ".join(args),
+ splitext(argv[-1])[0])
+ print "Compression:", on_off(options.compression)
+ print "Hardlink:", on_off(options.hardlink)
+
+ tmp_dir = "%s/perfdir.%d" % (abspath(options.directory), getpid())
+ recreate_dir(tmp_dir)
+ result = test(tmp_dir, options, args[:-1], args[-1])
+ rmtree(tmp_dir)
+ print_result(result)
+
+main(sys.argv)
+++ /dev/null
-#! /bin/sh
-
-set -e
-
-create_src() {
- n=$1
- i=0
- while [ $i -lt $n ]; do
- file=$i.cc
- cat <<EOF >$file
-#include <algorithm>
-#include <deque>
-#include <iostream>
-#include <list>
-#include <map>
-#include <set>
-#include <sstream>
-#include <string>
-#include <vector>
-int var$i;
-EOF
- i=$(($i + 1))
- done
-}
-
-compile() {
- local n=$1
- local compiler="$2"
- local i=0
- local objdir=objs
- rm -rf $objdir
- mkdir -p $objdir
- while [ $i -lt $n ]; do
- echo -n .
- $compiler -c $i.cc -O2 -o $objdir/$i.o
- i=$(($i + 1))
- done
- echo
-}
-
-now() {
- perl -e 'use Time::HiRes qw(time); print time'
-}
-
-elapsed() {
- perl -e 'use Time::HiRes qw(time); printf("%.3f\n", time - $ARGV[0])' $1
-}
-
-stat() {
- local desc="$1"
- local time=$2
- local ref_time=$3
- local perc=$(perl -e "print 100 * $time / $ref_time")
- local factor=$(perl -e "print $ref_time / $time")
- printf "%-43s %5.2f s (%6.2f %%) (%5.2f x)\n" "$desc:" $time $perc $factor
-}
-
-###############################################################################
-
-if [ -n "$CXX" ]; then
- cxx="$CXX"
-else
- cxx=/usr/bin/c++
-fi
-
-ccache=../ccache
-tmpdir=tmpdir.$$
-CCACHE_DIR=.ccache
-export CCACHE_DIR
-CCACHE_NODIRECT=1
-export CCACHE_NODIRECT
-
-rm -rf $tmpdir
-mkdir $tmpdir
-cd $tmpdir
-
-if [ "$#" -gt 0 ]; then
- n=$1
-else
- n=30
-fi
-create_src $n
-
-# Perform a compilation to warm up the disk cache.
-compile 1 $cxx >/dev/null
-
-echo "Without ccache:"
-t0=$(now)
-compile $n $cxx
-t_wo=$(elapsed $t0)
-
-echo "With ccache, no direct, cache miss:"
-t0=$(now)
-compile $n "$ccache $cxx"
-t_p_m=$(elapsed $t0)
-
-echo "With ccache, no direct, cache hit:"
-t0=$(now)
-compile $n "$ccache $cxx"
-t_p_h=$(elapsed $t0)
-
-unset CCACHE_NODIRECT
-rm -rf $CCACHE_DIR
-
-echo "With ccache, direct, cache miss:"
-t0=$(now)
-compile $n "$ccache $cxx"
-t_d_m=$(elapsed $t0)
-
-echo "With ccache, direct, cache hit:"
-t0=$(now)
-compile $n "$ccache $cxx"
-t_d_h=$(elapsed $t0)
-
-echo
-stat "Without ccache" $t_wo $t_wo
-stat "With ccache, preprocessor mode, cache miss" $t_p_m $t_wo
-stat "With ccache, preprocessor mode, cache hit" $t_p_h $t_wo
-stat "With ccache, direct mode, cache miss" $t_d_m $t_wo
-stat "With ccache, direct mode, cache hit" $t_d_h $t_wo