summaryrefslogtreecommitdiffstats
path: root/Tools/pybench/pybench.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/pybench/pybench.py')
-rwxr-xr-xTools/pybench/pybench.py934
1 files changed, 678 insertions, 256 deletions
diff --git a/Tools/pybench/pybench.py b/Tools/pybench/pybench.py
index e0110d0..8ff16c5 100755
--- a/Tools/pybench/pybench.py
+++ b/Tools/pybench/pybench.py
@@ -34,20 +34,7 @@ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
"""
-# Version number
-__version__ = '1.3'
-
-#
-# NOTE: Use xrange for all test loops unless you want to face
-# a 20MB process !
-#
-# All tests should have rounds set to values so that a run()
-# takes between 20-50 seconds. This is to get fairly good
-# clock() values. You can use option -w to speedup the tests
-# by a fixed integer factor (the "warp factor").
-#
-
-import sys,time,operator
+import sys, time, operator, string
from CommandLine import *
try:
@@ -56,6 +43,111 @@ try:
except ImportError:
import pickle
+# Version number; version history: see README file !
+__version__ = '2.0'
+
+### Constants
+
+# Second fractions
+MILLI_SECONDS = 1e3
+MICRO_SECONDS = 1e6
+
+# Percent unit
+PERCENT = 100
+
+# Horizontal line length
+LINE = 79
+
+# Minimum test run-time
+MIN_TEST_RUNTIME = 1e-3
+
+# Number of calibration runs to use for calibrating the tests
+CALIBRATION_RUNS = 20
+
+# Number of calibration loops to run for each calibration run
+CALIBRATION_LOOPS = 20
+
+# Allow skipping calibration ?
+ALLOW_SKIPPING_CALIBRATION = 1
+
+# Timer types
+TIMER_TIME_TIME = 'time.time'
+TIMER_TIME_CLOCK = 'time.clock'
+TIMER_SYSTIMES_PROCESSTIME = 'systimes.processtime'
+
+# Choose platform default timer
+if sys.platform[:3] == 'win':
+ # On WinXP this has 2.5ms resolution
+ TIMER_PLATFORM_DEFAULT = TIMER_TIME_CLOCK
+else:
+ # On Linux this has 1ms resolution
+ TIMER_PLATFORM_DEFAULT = TIMER_TIME_TIME
+
+# Print debug information ?
+_debug = 0
+
+### Helpers
+
+def get_timer(timertype):
+
+ if timertype == TIMER_TIME_TIME:
+ return time.time
+ elif timertype == TIMER_TIME_CLOCK:
+ return time.clock
+ elif timertype == TIMER_SYSTIMES_PROCESSTIME:
+ import systimes
+ return systimes.processtime
+ else:
+ raise TypeError('unknown timer type: %s' % timertype)
+
+def get_machine_details():
+
+ import platform
+ if _debug:
+ print 'Getting machine details...'
+ buildno, builddate = platform.python_build()
+ python = platform.python_version()
+ if python > '2.0':
+ try:
+ unichr(100000)
+ except ValueError:
+ # UCS2 build (standard)
+ unicode = 'UCS2'
+ else:
+ # UCS4 build (most recent Linux distros)
+ unicode = 'UCS4'
+ else:
+ unicode = None
+ bits, linkage = platform.architecture()
+ return {
+ 'platform': platform.platform(),
+ 'processor': platform.processor(),
+ 'executable': sys.executable,
+ 'python': platform.python_version(),
+ 'compiler': platform.python_compiler(),
+ 'buildno': buildno,
+ 'builddate': builddate,
+ 'unicode': unicode,
+ 'bits': bits,
+ }
+
+def print_machine_details(d, indent=''):
+
+ l = ['Machine Details:',
+ ' Platform ID: %s' % d.get('platform', 'n/a'),
+ ' Processor: %s' % d.get('processor', 'n/a'),
+ '',
+ 'Python:',
+ ' Executable: %s' % d.get('executable', 'n/a'),
+ ' Version: %s' % d.get('python', 'n/a'),
+ ' Compiler: %s' % d.get('compiler', 'n/a'),
+ ' Bits: %s' % d.get('bits', 'n/a'),
+ ' Build: %s (#%s)' % (d.get('builddate', 'n/a'),
+ d.get('buildno', 'n/a')),
+ ' Unicode: %s' % d.get('unicode', 'n/a'),
+ ]
+ print indent + string.join(l, '\n' + indent) + '\n'
+
### Test baseclass
class Test:
@@ -84,7 +176,7 @@ class Test:
# Version number of the test as float (x.yy); this is important
# for comparisons of benchmark runs - tests with unequal version
# number will not get compared.
- version = 1.0
+ version = 2.0
# The number of abstract operations done in each round of the
# test. An operation is the basic unit of what you want to
@@ -97,36 +189,125 @@ class Test:
# Number of rounds to execute per test run. This should be
# adjusted to a figure that results in a test run-time of between
- # 20-50 seconds.
- rounds = 10000
+ # 1-2 seconds.
+ rounds = 100000
### Internal variables
# Mark this class as implementing a test
is_a_test = 1
- # Misc. internal variables
- last_timing = (0,0,0) # last timing (real,run,calibration)
- warp = 1 # warp factor this test uses
- cruns = 20 # number of calibration runs
- overhead = None # list of calibration timings
+ # Last timing: (real, run, overhead)
+ last_timing = (0.0, 0.0, 0.0)
+
+ # Warp factor to use for this test
+ warp = 1
- def __init__(self,warp=1):
+ # Number of calibration runs to use
+ calibration_runs = CALIBRATION_RUNS
- if warp > 1:
- self.rounds = self.rounds / warp
+ # List of calibration timings
+ overhead_times = None
+
+ # List of test run timings
+ times = []
+
+ # Timer used for the benchmark
+ timer = TIMER_PLATFORM_DEFAULT
+
+ def __init__(self, warp=None, calibration_runs=None, timer=None):
+
+ # Set parameters
+ if warp is not None:
+ self.rounds = int(self.rounds / warp)
if self.rounds == 0:
- self.rounds = 1
+ raise ValueError('warp factor set too high')
self.warp = warp
+ if calibration_runs is not None:
+ if (not ALLOW_SKIPPING_CALIBRATION and
+ calibration_runs < 1):
+ raise ValueError('at least one calibration run is required')
+ self.calibration_runs = calibration_runs
+ if timer is not None:
+ timer = timer
+
+ # Init variables
self.times = []
- self.overhead = []
+ self.overhead_times = []
+
# We want these to be in the instance dict, so that pickle
# saves them
self.version = self.version
self.operations = self.operations
self.rounds = self.rounds
- def run(self, cruns):
+ def get_timer(self):
+
+ """ Return the timer function to use for the test.
+
+ """
+ return get_timer(self.timer)
+
+ def compatible(self, other):
+
+ """ Return 1/0 depending on whether the test is compatible
+ with the other Test instance or not.
+
+ """
+ if self.version != other.version:
+ return 0
+ if self.rounds != other.rounds:
+ return 0
+ return 1
+
+ def calibrate_test(self):
+
+ if self.calibration_runs == 0:
+ self.overhead_times = [0.0]
+ return
+
+ calibrate = self.calibrate
+ timer = self.get_timer()
+ calibration_loops = range(CALIBRATION_LOOPS)
+
+ # Time the calibration loop overhead
+ prep_times = []
+ for i in range(self.calibration_runs):
+ t = timer()
+ for i in calibration_loops:
+ pass
+ t = timer() - t
+ prep_times.append(t)
+ min_prep_time = min(prep_times)
+ if _debug:
+ print
+ print 'Calib. prep time = %.6fms' % (
+ min_prep_time * MILLI_SECONDS)
+
+ # Time the calibration runs (doing CALIBRATION_LOOPS loops of
+ # .calibrate() method calls each)
+ for i in range(self.calibration_runs):
+ t = timer()
+ for i in calibration_loops:
+ calibrate()
+ t = timer() - t
+ self.overhead_times.append(t / CALIBRATION_LOOPS
+ - min_prep_time)
+
+ # Check the measured times
+ min_overhead = min(self.overhead_times)
+ max_overhead = max(self.overhead_times)
+ if _debug:
+ print 'Calib. overhead time = %.6fms' % (
+ min_overhead * MILLI_SECONDS)
+ if min_overhead < 0.0:
+ raise ValueError('calibration setup did not work')
+ if max_overhead - min_overhead > 0.1:
+ raise ValueError(
+ 'overhead calibration timing range too inaccurate: '
+ '%r - %r' % (min_overhead, max_overhead))
+
+ def run(self):
""" Run the test in two phases: first calibrate, then
do the actual test. Be careful to keep the calibration
@@ -134,27 +315,23 @@ class Test:
"""
test = self.test
- calibrate = self.calibrate
- clock = time.clock
- # first calibrate
- t = clock()
- calibrate()
- offset = clock() - t
- if cruns:
- for i in range(cruns-1):
- t = clock()
- calibrate()
- t = clock() - t
- if t < offset:
- offset = t
- # now the real thing
- t = clock()
+ timer = self.get_timer()
+
+ # Get calibration
+ min_overhead = min(self.overhead_times)
+
+ # Test run
+ t = timer()
test()
- t = clock() - t
- if t < 0.01:
- sys.exit("Lower warp required: test times < 10 ms are unreliable")
- self.last_timing = (t-offset,t,offset)
- self.times.append(t-offset)
+ t = timer() - t
+ if t < MIN_TEST_RUNTIME:
+ raise ValueError('warp factor too high: '
+ 'test times are < 10ms')
+ eff_time = t - min_overhead
+ if eff_time < 0:
+ raise ValueError('wrong calibration')
+ self.last_timing = (eff_time, t, min_overhead)
+ self.times.append(eff_time)
def calibrate(self):
@@ -176,33 +353,33 @@ class Test:
self.operations number of operations each.
"""
- # do some tests
return
def stat(self):
- """ Returns four values:
- minimum round time
- average time per round
- average time per operation
- average overhead time
+ """ Return test run statistics as tuple:
+
+ (minimum run time,
+ average run time,
+ total run time,
+ average time per operation,
+ minimum overhead time)
- XXX Should this take warp factors into account?
"""
runs = len(self.times)
if runs == 0:
- return 0,0
- mintime = min(self.times)
- totaltime = reduce(operator.add,self.times,0.0)
- avg = totaltime / float(runs)
- op_avg = totaltime / float(runs * self.rounds * self.operations)
- if self.overhead:
- totaloverhead = reduce(operator.add,self.overhead,0.0)
- ov_avg = totaloverhead / float(runs)
+ return 0.0, 0.0, 0.0, 0.0
+ min_time = min(self.times)
+ total_time = reduce(operator.add, self.times, 0.0)
+ avg_time = total_time / float(runs)
+ operation_avg = total_time / float(runs
+ * self.rounds
+ * self.operations)
+ if self.overhead_times:
+ min_overhead = min(self.overhead_times)
else:
- # use self.last_timing - not too accurate
- ov_avg = self.last_timing[2]
- return mintime, avg, op_avg, ov_avg
+ min_overhead = self.last_timing[2]
+ return min_time, avg_time, total_time, operation_avg, min_overhead
### Load Setup
@@ -215,153 +392,353 @@ import Setup
class Benchmark:
- name = '?' # Name of the benchmark
- rounds = 1 # Number of rounds to run
+ # Name of the benchmark
+ name = ''
+
+ # Number of benchmark rounds to run
+ rounds = 1
+
+ # Warp factor use to run the tests
warp = 1 # Warp factor
- roundtime = 0 # Average round time
- version = None # Benchmark version number (see __init__)
- # as float x.yy
- def __init__(self):
+ # Average benchmark round time
+ roundtime = 0
- self.tests = {}
- self.version = 0.31
+ # Benchmark version number as float x.yy
+ version = 2.0
- def load_tests(self, setupmod, warp=1, limitnames="", verbose=0):
+ # Produce verbose output ?
+ verbose = 0
- self.warp = warp
- if limitnames:
- limitnames = re.compile(limitnames, re.I)
+ # Dictionary with the machine details
+ machine_details = None
+
+ # Timer used for the benchmark
+ timer = TIMER_PLATFORM_DEFAULT
+
+ def __init__(self, name, verbose=None, timer=None, warp=None,
+ calibration_runs=None):
+
+ if name:
+ self.name = name
else:
- limitnames = None
- tests = self.tests
- if verbose:
- print 'Searching for tests ...',
- setupmod.__dict__.values()
- for c in setupmod.__dict__.values():
- if not hasattr(c,'is_a_test'):
+ self.name = '%04i-%02i-%02i %02i:%02i:%02i' % \
+ (time.localtime(time.time())[:6])
+ if verbose is not None:
+ self.verbose = verbose
+ if timer is not None:
+ self.timer = timer
+ if warp is not None:
+ self.warp = warp
+ if calibration_runs is not None:
+ self.calibration_runs = calibration_runs
+
+ # Init vars
+ self.tests = {}
+ if _debug:
+ print 'Getting machine details...'
+ self.machine_details = get_machine_details()
+
+ # Make .version an instance attribute to have it saved in the
+ # Benchmark pickle
+ self.version = self.version
+
+ def get_timer(self):
+
+ """ Return the timer function to use for the test.
+
+ """
+ return get_timer(self.timer)
+
+ def compatible(self, other):
+
+ """ Return 1/0 depending on whether the benchmark is
+ compatible with the other Benchmark instance or not.
+
+ """
+ if self.version != other.version:
+ return 0
+ if (self.machine_details == other.machine_details and
+ self.timer != other.timer):
+ return 0
+ if (self.calibration_runs == 0 and
+ other.calibration_runs != 0):
+ return 0
+ if (self.calibration_runs != 0 and
+ other.calibration_runs == 0):
+ return 0
+ return 1
+
+ def load_tests(self, setupmod, limitnames=None):
+
+ # Add tests
+ if self.verbose:
+ print 'Searching for tests ...'
+ print '--------------------------------------'
+ for testclass in setupmod.__dict__.values():
+ if not hasattr(testclass, 'is_a_test'):
continue
- name = c.__name__
+ name = testclass.__name__
if name == 'Test':
continue
- if limitnames is not None and limitnames.search(name) is None:
+ if (limitnames is not None and
+ limitnames.search(name) is None):
continue
- tests[name] = c(warp)
- l = tests.keys()
+ self.tests[name] = testclass(
+ warp=self.warp,
+ calibration_runs=self.calibration_runs,
+ timer=self.timer)
+ l = self.tests.keys()
l.sort()
- if verbose:
+ if self.verbose:
+ for name in l:
+ print ' %s' % name
+ print '--------------------------------------'
+ print ' %i tests found' % len(l)
+ print
+
+ def calibrate(self):
+
+ print 'Calibrating tests. Please wait...'
+ if self.verbose:
print
- for t in l:
- print ' ', t
- print len(l), "tests found"
+ print 'Test min max'
+ print '-' * LINE
+ tests = self.tests.items()
+ tests.sort()
+ for i in range(len(tests)):
+ name, test = tests[i]
+ test.calibrate_test()
+ if self.verbose:
+ print '%30s: %6.3fms %6.3fms' % \
+ (name,
+ min(test.overhead_times) * MILLI_SECONDS,
+ max(test.overhead_times) * MILLI_SECONDS)
print
- def run(self, verbose, cruns):
+ def run(self):
tests = self.tests.items()
tests.sort()
- clock = time.clock
- print 'Running %i round(s) of the suite at warp factor %i:' % (self.rounds, self.warp)
+ timer = self.get_timer()
+ print 'Running %i round(s) of the suite at warp factor %i:' % \
+ (self.rounds, self.warp)
print
- roundtime = clock()
+ self.roundtimes = []
for i in range(self.rounds):
- roundstarttime = clock()
- if verbose:
- print ' Round %-25i real abs overhead' % (i+1)
+ if self.verbose:
+ print ' Round %-25i effective absolute overhead' % (i+1)
+ total_eff_time = 0.0
for j in range(len(tests)):
- name, t = tests[j]
- if verbose:
+ name, test = tests[j]
+ if self.verbose:
print '%30s:' % name,
- t.run(cruns)
- if verbose:
- print ' %.3fr %.3fa %.3fo' % t.last_timing
- if verbose:
- print ' ----------------------'
- print ' Average round time: %.3f seconds' % \
- ((clock() - roundtime)/(i+1))
+ test.run()
+ (eff_time, abs_time, min_overhead) = test.last_timing
+ total_eff_time = total_eff_time + eff_time
+ if self.verbose:
+ print ' %5.0fms %5.0fms %7.3fms' % \
+ (eff_time * MILLI_SECONDS,
+ abs_time * MILLI_SECONDS,
+ min_overhead * MILLI_SECONDS)
+ self.roundtimes.append(total_eff_time)
+ if self.verbose:
+ print (' '
+ ' ------------------------------')
+ print (' '
+ ' Totals: %6.0fms' %
+ (total_eff_time * MILLI_SECONDS))
print
else:
- print '%d done in %.3f seconds' % (i+1, (clock() - roundstarttime))
- self.roundtime = (clock() - roundtime) / self.rounds
+ print '* Round %i done in %.3f seconds.' % (i+1,
+ total_eff_time)
print
- def print_stat(self, compare_to=None, hidenoise=0):
-
- if not compare_to:
- print '%-30s min run avg run per oprn overhead' % 'Tests:'
- print '-'*77
- tests = self.tests.items()
- tests.sort()
- totalmintime = 0
- for name,t in tests:
- mintime,avg,op_avg,ov_avg = t.stat()
- totalmintime += mintime
- print '%30s: %9.2f ms %9.2f ms %6.2f us %6.2f' % \
- (name,mintime*1000.0,avg*1000.0,op_avg*1000000.0,ov_avg*1000.0)
- print '-'*77
- print '%30s: %9.2f ms' % \
- ('Notional minimum round time', totalmintime * 1000.0)
+ def stat(self):
- else:
- print 'Comparing with: %s (rounds=%i, warp=%i)' % \
- (compare_to.name,compare_to.rounds,compare_to.warp)
- print '%-30s min run cmp run avg run diff' % \
- 'Tests:'
- print '-'*77
- tests = self.tests.items()
- tests.sort()
- compatible = 1
- totalmintime = other_totalmintime = 0
- for name, t in tests:
- mintime, avg, op_avg, ov_avg = t.stat()
- totalmintime += mintime
- try:
- other = compare_to.tests[name]
- except KeyError:
- other = None
- if other and other.version == t.version and \
- other.operations == t.operations:
- mintime1, avg1, op_avg1, ov_avg1 = other.stat()
- other_totalmintime += mintime1
- diff = ((mintime*self.warp)/(mintime1*other.warp) - 1.0)*100.0
- if hidenoise and abs(qop_avg) < 10:
- diff = ''
+ """ Return benchmark run statistics as tuple:
+
+ (minimum round time,
+ average round time,
+ maximum round time)
+
+ XXX Currently not used, since the benchmark does test
+ statistics across all rounds.
+
+ """
+ runs = len(self.roundtimes)
+ if runs == 0:
+ return 0.0, 0.0
+ min_time = min(self.roundtimes)
+ total_time = reduce(operator.add, self.roundtimes, 0.0)
+ avg_time = total_time / float(runs)
+ max_time = max(self.roundtimes)
+ return (min_time, avg_time, max_time)
+
+ def print_header(self, title='Benchmark'):
+
+ print '-' * LINE
+ print '%s: %s' % (title, self.name)
+ print '-' * LINE
+ print
+ print ' Rounds: %s' % self.rounds
+ print ' Warp: %s' % self.warp
+ print ' Timer: %s' % self.timer
+ print
+ if self.machine_details:
+ print_machine_details(self.machine_details, indent=' ')
+ print
+
+ def print_benchmark(self, hidenoise=0, limitnames=None):
+
+ print ('Test '
+ ' minimum average operation overhead')
+ print '-' * LINE
+ tests = self.tests.items()
+ tests.sort()
+ total_min_time = 0.0
+ total_avg_time = 0.0
+ for name, test in tests:
+ if (limitnames is not None and
+ limitnames.search(name) is None):
+ continue
+ (min_time,
+ avg_time,
+ total_time,
+ op_avg,
+ min_overhead) = test.stat()
+ total_min_time = total_min_time + min_time
+ total_avg_time = total_avg_time + avg_time
+ print '%30s: %5.0fms %5.0fms %6.2fus %7.3fms' % \
+ (name,
+ min_time * MILLI_SECONDS,
+ avg_time * MILLI_SECONDS,
+ op_avg * MICRO_SECONDS,
+ min_overhead *MILLI_SECONDS)
+ print '-' * LINE
+ print ('Totals: '
+ ' %6.0fms %6.0fms' %
+ (total_min_time * MILLI_SECONDS,
+ total_avg_time * MILLI_SECONDS,
+ ))
+ print
+
+ def print_comparison(self, compare_to, hidenoise=0, limitnames=None):
+
+ # Check benchmark versions
+ if compare_to.version != self.version:
+ print ('* Benchmark versions differ: '
+ 'cannot compare this benchmark to "%s" !' %
+ compare_to.name)
+ print
+ self.print_benchmark(hidenoise=hidenoise,
+ limitnames=limitnames)
+ return
+
+ # Print header
+ compare_to.print_header('Comparing with')
+ print ('Test '
+ ' minimum run-time average run-time')
+ print (' '
+ ' this other diff this other diff')
+ print '-' * LINE
+
+ # Print test comparisons
+ tests = self.tests.items()
+ tests.sort()
+ total_min_time = other_total_min_time = 0.0
+ total_avg_time = other_total_avg_time = 0.0
+ benchmarks_compatible = self.compatible(compare_to)
+ tests_compatible = 1
+ for name, test in tests:
+ if (limitnames is not None and
+ limitnames.search(name) is None):
+ continue
+ (min_time,
+ avg_time,
+ total_time,
+ op_avg,
+ min_overhead) = test.stat()
+ total_min_time = total_min_time + min_time
+ total_avg_time = total_avg_time + avg_time
+ try:
+ other = compare_to.tests[name]
+ except KeyError:
+ other = None
+ if other is None:
+ # Other benchmark doesn't include the given test
+ min_diff, avg_diff = 'n/a', 'n/a'
+ other_min_time = 0.0
+ other_avg_time = 0.0
+ tests_compatible = 0
+ else:
+ (other_min_time,
+ other_avg_time,
+ other_total_time,
+ other_op_avg,
+ other_min_overhead) = other.stat()
+ other_total_min_time = other_total_min_time + other_min_time
+ other_total_avg_time = other_total_avg_time + other_avg_time
+ if (benchmarks_compatible and
+ test.compatible(other)):
+ # Both benchmark and tests are comparible
+ min_diff = ((min_time * self.warp) /
+ (other_min_time * other.warp) - 1.0)
+ avg_diff = ((avg_time * self.warp) /
+ (other_avg_time * other.warp) - 1.0)
+ if hidenoise and abs(min_diff) < 10.0:
+ min_diff = ''
+ else:
+ min_diff = '%+5.1f%%' % (min_diff * PERCENT)
+ if hidenoise and abs(avg_diff) < 10.0:
+ avg_diff = ''
else:
- diff = '%+7.2f%%' % diff
+ avg_diff = '%+5.1f%%' % (avg_diff * PERCENT)
else:
- qavg, diff = 'n/a', 'n/a'
- compatible = 0
- print '%30s: %8.2f ms %8.2f ms %8.2f ms %8s' % \
- (name,mintime*1000.0,mintime1*1000.0 * compare_to.warp/self.warp, avg*1000.0,diff)
- print '-'*77
- #
- # Summarise test results
- #
- if compatible and compare_to.roundtime > 0 and \
- compare_to.version == self.version:
- print '%30s: %8.2f ms %8.2f ms %+7.2f%%' % \
- ('Notional minimum round time', totalmintime * 1000.0,
- other_totalmintime * 1000.0 * compare_to.warp/self.warp,
- ((totalmintime*self.warp)/
- (other_totalmintime*compare_to.warp)-1.0)*100.0)
+ # Benchmark or tests are not comparible
+ min_diff, avg_diff = 'n/a', 'n/a'
+ tests_compatible = 0
+ print '%30s: %5.0fms %5.0fms %7s %5.0fms %5.0fms %7s' % \
+ (name,
+ min_time * MILLI_SECONDS,
+ other_min_time * MILLI_SECONDS * compare_to.warp / self.warp,
+ min_diff,
+ avg_time * MILLI_SECONDS,
+ other_avg_time * MILLI_SECONDS * compare_to.warp / self.warp,
+ avg_diff)
+ print '-' * LINE
+
+ # Summarise test results
+ if not benchmarks_compatible or not tests_compatible:
+ min_diff, avg_diff = 'n/a', 'n/a'
+ else:
+ if other_total_min_time != 0.0:
+ min_diff = '%+5.1f%%' % (
+ ((total_min_time * self.warp) /
+ (other_total_min_time * compare_to.warp) - 1.0) * PERCENT)
+ else:
+ min_diff = 'n/a'
+ if other_total_avg_time != 0.0:
+ avg_diff = '%+5.1f%%' % (
+ ((total_avg_time * self.warp) /
+ (other_total_avg_time * compare_to.warp) - 1.0) * PERCENT)
else:
- print '%30s: %9.2f ms n/a' % \
- ('Notional minimum round time', totalmintime * 1000.0)
+ avg_diff = 'n/a'
+ print ('Totals: '
+ ' %5.0fms %5.0fms %7s %5.0fms %5.0fms %7s' %
+ (total_min_time * MILLI_SECONDS,
+ (other_total_min_time * compare_to.warp/self.warp
+ * MILLI_SECONDS),
+ min_diff,
+ total_avg_time * MILLI_SECONDS,
+ (other_total_avg_time * compare_to.warp/self.warp
+ * MILLI_SECONDS),
+ avg_diff
+ ))
+ print
+ print '(this=%s, other=%s)' % (self.name,
+ compare_to.name)
print
-
-def print_machine():
-
- import platform
- print 'Machine Details:'
- print ' Platform ID: %s' % platform.platform()
- print ' Executable: %s' % sys.executable
- # There's a bug in Python 2.2b1+...
- if sys.version[:6] == '2.2b1+':
- return
- print ' Python: %s' % platform.python_version()
- print ' Compiler: %s' % platform.python_compiler()
- buildno, builddate = platform.python_build()
- print ' Build: %s (#%s)' % (builddate, buildno)
class PyBenchCmdline(Application):
@@ -370,50 +747,64 @@ class PyBenchCmdline(Application):
version = __version__
- options = [ArgumentOption('-n','number of rounds',Setup.Number_of_rounds),
- ArgumentOption('-f','save benchmark to file arg',''),
- ArgumentOption('-c','compare benchmark with the one in file arg',''),
- ArgumentOption('-s','show benchmark in file arg, then exit',''),
- SwitchOption('-S','show statistics of benchmarks',0),
- ArgumentOption('-w','set warp factor to arg',Setup.Warp_factor),
- SwitchOption('-d','hide noise in compares', 0),
- SwitchOption('-v','verbose output (not recommended)', 0),
- SwitchOption('--no-gc','disable garbage collection', 0),
- SwitchOption('--no-syscheck',
- '"disable" sys check interval (set to sys.maxint)', 0),
- ArgumentOption('-t', 'tests containing substring', ''),
- ArgumentOption('-C', 'number of calibration runs', 20)
+ debug = _debug
+
+ options = [ArgumentOption('-n',
+ 'number of rounds',
+ Setup.Number_of_rounds),
+ ArgumentOption('-f',
+ 'save benchmark to file arg',
+ ''),
+ ArgumentOption('-c',
+ 'compare benchmark with the one in file arg',
+ ''),
+ ArgumentOption('-s',
+ 'show benchmark in file arg, then exit',
+ ''),
+ ArgumentOption('-w',
+ 'set warp factor to arg',
+ Setup.Warp_factor),
+ ArgumentOption('-t',
+ 'run only tests with names matching arg',
+ ''),
+ ArgumentOption('-C',
+ 'set the number of calibration runs to arg',
+ CALIBRATION_RUNS),
+ SwitchOption('-d',
+ 'hide noise in comparisons',
+ 0),
+ SwitchOption('-v',
+ 'verbose output (not recommended)',
+ 0),
+ SwitchOption('--with-gc',
+ 'enable garbage collection',
+ 0),
+ SwitchOption('--with-syscheck',
+ 'use default sys check interval',
+ 0),
+ ArgumentOption('--timer',
+ 'use given timer',
+ TIMER_PLATFORM_DEFAULT),
]
about = """\
The normal operation is to run the suite and display the
-results. Use -f to save them for later reuse or comparisms.
+results. Use -f to save them for later reuse or comparisons.
+
+Available timers:
+
+ time.time
+ time.clock
+ systimes.processtime
Examples:
-python1.5 pybench.py -w 100 -f p15
-python1.4 pybench.py -w 100 -f p14
-python pybench.py -s p15 -c p14
+python2.1 pybench.py -f p21.pybench
+python2.5 pybench.py -f p25.pybench
+python pybench.py -s p25.pybench -c p21.pybench
"""
copyright = __copyright__
- def handle_S(self, value):
-
- """ Display one line stats for each benchmark file given on the
- command line.
-
- """
- for benchmark in self.files:
- try:
- f = open(benchmark, 'rb')
- bench = pickle.load(f)
- f.close()
- except IOError:
- print '* Error opening/reading file %s' % repr(benchmark)
- else:
- print '%s,%-.2f,ms' % (benchmark, bench.roundtime*1000.0)
- return 0
-
def main(self):
rounds = self.values['-n']
@@ -421,38 +812,52 @@ python pybench.py -s p15 -c p14
show_bench = self.values['-s']
compare_to = self.values['-c']
hidenoise = self.values['-d']
- warp = self.values['-w']
- nogc = self.values['--no-gc']
+ warp = int(self.values['-w'])
+ withgc = self.values['--with-gc']
limitnames = self.values['-t']
+ if limitnames:
+ if _debug:
+ print '* limiting test names to one with substring "%s"' % \
+ limitnames
+ limitnames = re.compile(limitnames, re.I)
+ else:
+ limitnames = None
verbose = self.verbose
- nosyscheck = self.values['--no-syscheck']
- cruns = self.values['-C']
- print "CRUNS:", cruns
+ withsyscheck = self.values['--with-syscheck']
+ calibration_runs = self.values['-C']
+ timer = self.values['--timer']
- print 'PYBENCH',__version__
+ print '-' * LINE
+ print 'PYBENCH %s' % __version__
+ print '-' * LINE
+ print '* using Python %s' % (string.split(sys.version)[0])
- # Switch off GC
- if nogc:
+ # Switch off garbage collection
+ if not withgc:
try:
import gc
except ImportError:
- nogc = 0
+ print '* Python version doesn\'t support garbage collection'
else:
- if self.values['--no-gc']:
- gc.disable()
- print 'NO GC'
-
- # maximise sys check interval
- if nosyscheck:
- sys.setcheckinterval(sys.maxint)
- print 'CHECKINTERVAL =', sys.maxint
+ gc.disable()
+ print '* disabled garbage collection'
+
+ # "Disable" sys check interval
+ if not withsyscheck:
+ # Too bad the check interval uses an int instead of a long...
+ value = 2147483647
+ sys.setcheckinterval(value)
+ print '* system check interval set to maximum: %s' % value
+
+ if timer == TIMER_SYSTIMES_PROCESSTIME:
+ import systimes
+ print '* using timer: systimes.processtime (%s)' % \
+ systimes.SYSTIMES_IMPLEMENTATION
+ else:
+ print '* using timer: %s' % timer
print
- if not compare_to:
- print_machine()
- print
-
if compare_to:
try:
f = open(compare_to,'rb')
@@ -460,8 +865,10 @@ python pybench.py -s p15 -c p14
bench.name = compare_to
f.close()
compare_to = bench
- except IOError:
- print '* Error opening/reading file',compare_to
+ except IOError, reason:
+ print '* Error opening/reading file %s: %s' % (
+ repr(compare_to),
+ reason)
compare_to = None
if show_bench:
@@ -470,37 +877,52 @@ python pybench.py -s p15 -c p14
bench = pickle.load(f)
bench.name = show_bench
f.close()
- print 'Benchmark: %s (rounds=%i, warp=%i)' % \
- (bench.name,bench.rounds,bench.warp)
- print
- bench.print_stat(compare_to, hidenoise)
+ bench.print_header()
+ if compare_to:
+ bench.print_comparison(compare_to,
+ hidenoise=hidenoise,
+ limitnames=limitnames)
+ else:
+ bench.print_benchmark(hidenoise=hidenoise,
+ limitnames=limitnames)
except IOError:
- print '* Error opening/reading file',show_bench
+ print '* Error opening/reading file %s: %s' % (
+ repr(show_bench),
+ reason)
print
return
if reportfile:
- if nogc:
- print 'Benchmark: %s (rounds=%i, warp=%i, no GC)' % \
- (reportfile,rounds,warp)
- else:
- print 'Benchmark: %s (rounds=%i, warp=%i)' % \
- (reportfile,rounds,warp)
+ print 'Creating benchmark: %s (rounds=%i, warp=%i)' % \
+ (reportfile, rounds, warp)
print
# Create benchmark object
- bench = Benchmark()
+ bench = Benchmark(reportfile,
+ verbose=verbose,
+ timer=timer,
+ warp=warp,
+ calibration_runs=calibration_runs)
bench.rounds = rounds
- bench.load_tests(Setup, warp, limitnames, verbose)
+ bench.load_tests(Setup, limitnames=limitnames)
try:
- bench.run(verbose, cruns)
+ bench.calibrate()
+ bench.run()
except KeyboardInterrupt:
print
print '*** KeyboardInterrupt -- Aborting'
print
return
- bench.print_stat(compare_to)
- # ring bell
+ bench.print_header()
+ if compare_to:
+ bench.print_comparison(compare_to,
+ hidenoise=hidenoise,
+ limitnames=limitnames)
+ else:
+ bench.print_benchmark(hidenoise=hidenoise,
+ limitnames=limitnames)
+
+ # Ring bell
sys.stderr.write('\007')
if reportfile: