http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / bench / bench.py
1 #!/usr/bin/env python
2 #
3 # __COPYRIGHT__
4 #
5 # A script for timing snippets of Python code.
6 #
7 # By default, this script will execute a single Python file specified on
8 # the command line and time any functions in a list named "FunctionList"
9 # set by the Python file under test, or (by default) time any functions
10 # in the file whose names begin with "Func".
11 #
12 # All functions are assumed to get passed the same arguments, and the
13 # inputs are specified in a list named "Data," each element of which
14 # is a list consisting of a tag name, a list of positional arguments,
15 # and a dictionary of keyword arguments.
16 #
17 # Each function is expected to test a single, comparable snippet of
18 # of Python code.  IMPORTANT:  We want to test the timing of the code
19 # itself, not Python function call overhead, so every function should
20 # put its code under test within the following block:
21 #
22 #       for i in IterationList:
23 #
24 # This will allow (as much as possible) us to time just the code itself,
25 # not Python function call overhead.
26 from __future__ import generators  ### KEEP FOR COMPATIBILITY FIXERS
27
28 import getopt
29 import sys
30 import time
31 import types
32
33 Usage = """\
34 Usage:  bench.py OPTIONS file.py
35   --clock                       Use the time.clock function
36   --func PREFIX                 Test functions whose names begin with PREFIX
37   -h, --help                    Display this help and exit
38   -i ITER, --iterations ITER    Run each code snippet ITER times
39   --time                        Use the time.time function
40   -r RUNS, --runs RUNS          Average times for RUNS invocations of 
41 """
42
43 # How many times each snippet of code will be (or should be) run by the 
44 # functions under test to gather the time (the "inner loop").
45
46 Iterations = 1000
47
48 # How many times we'll run each function to collect its aggregate time
49 # and try to average out timing differences induced by system performance
50 # (the "outer loop").
51
52 Runs = 10
53
54 # The prefix of the functions under test.  This will be used if
55 # there's no explicit list defined in FunctionList.
56
57 FunctionPrefix = 'Func'
58
59 # The function used to get the current time.  The default of time.time is
60 # good on most UNIX systems, but time.clock (selectable via the --clock
61 # option) is better on Windows and some other UNIX systems.
62
63 Now = time.time
64
65
66 opts, args = getopt.getopt(sys.argv[1:], 'hi:r:',
67                            ['clock', 'func=', 'help',
68                             'iterations=', 'time', 'runs='])
69
70 for o, a in opts:
71     if o in ['--clock']:
72         Now = time.clock
73     elif o in ['--func']:
74         FunctionPrefix = a
75     elif o in ['-h', '--help']:
76         sys.stdout.write(Usage)
77         sys.exit(0)
78     elif o in ['-i', '--iterations']:
79         Iterations = int(a)
80     elif o in ['--time']:
81         Now = time.time
82     elif o in ['-r', '--runs']:
83         Runs = int(a)
84
85 if len(args) != 1:
86     sys.stderr.write("bench.py:  only one file argument must be specified\n")
87     sys.stderr.write(Usage)
88     sys.exit(1)
89
90
91 exec(open(args[0], 'rU').read())
92
93
94 try:
95     FunctionList
96 except NameError:
97     function_names = sorted([x for x in locals().keys() if x[:4] == FunctionPrefix])
98     l = [locals()[f] for f in function_names]
99     FunctionList = [f for f in l if isinstance(f, types.FunctionType)]
100
101 IterationList = [None] * Iterations
102
103 def timer(func, *args, **kw):
104     results = []
105     for i in range(Runs):
106         start = Now()
107         func(*args, **kw)
108         finish = Now()
109         results.append((finish - start) / Iterations)
110     return results
111
112 def display(label, results):
113     total = reduce(lambda x, y: x+y, results, 0.0)
114     print "    %8.3f" % ((total * 1e6) / len(results)), ':', label
115
116 for func in FunctionList:
117     if func.__doc__: d = ' (' + func.__doc__ + ')'
118     else: d = ''
119     print func.__name__ + d + ':'
120
121     for label, args, kw in Data:
122         r = timer(func, *args, **kw)
123         display(label, r)
124
125 # Local Variables:
126 # tab-width:4
127 # indent-tabs-mode:nil
128 # End:
129 # vim: set expandtab tabstop=4 shiftwidth=4: