[Python-checkins] r46928 - python/trunk/Tools/pybench/Arithmetic.py python/trunk/Tools/pybench/Calls.py python/trunk/Tools/pybench/CommandLine.py python/trunk/Tools/pybench/Constructs.py python/trunk/Tools/pybench/Dict.py python/trunk/Tools/pybench/Exceptions.py python/trunk/Tools/pybench/Imports.py python/trunk/Tools/pybench/Instances.py python/trunk/Tools/pybench/Lists.py python/trunk/Tools/pybench/Lookups.py python/trunk/Tools/pybench/NewInstances.py python/trunk/Tools/pybench/Numbers.py python/trunk/Tools/pybench/README python/trunk/Tools/pybench/Setup.py python/trunk/Tools/pybench/Strings.py python/trunk/Tools/pybench/Tuples.py python/trunk/Tools/pybench/Unicode.py python/trunk/Tools/pybench/clockres.py python/trunk/Tools/pybench/pybench.py python/trunk/Tools/pybench/systimes.py

marc-andre.lemburg python-checkins at python.org
Tue Jun 13 20:56:57 CEST 2006


Author: marc-andre.lemburg
Date: Tue Jun 13 20:56:56 2006
New Revision: 46928

Added:
   python/trunk/Tools/pybench/clockres.py   (contents, props changed)
Modified:
   python/trunk/Tools/pybench/Arithmetic.py
   python/trunk/Tools/pybench/Calls.py
   python/trunk/Tools/pybench/CommandLine.py
   python/trunk/Tools/pybench/Constructs.py
   python/trunk/Tools/pybench/Dict.py
   python/trunk/Tools/pybench/Exceptions.py
   python/trunk/Tools/pybench/Imports.py
   python/trunk/Tools/pybench/Instances.py
   python/trunk/Tools/pybench/Lists.py
   python/trunk/Tools/pybench/Lookups.py
   python/trunk/Tools/pybench/NewInstances.py   (contents, props changed)
   python/trunk/Tools/pybench/Numbers.py
   python/trunk/Tools/pybench/README
   python/trunk/Tools/pybench/Setup.py
   python/trunk/Tools/pybench/Strings.py
   python/trunk/Tools/pybench/Tuples.py
   python/trunk/Tools/pybench/Unicode.py
   python/trunk/Tools/pybench/pybench.py
   python/trunk/Tools/pybench/systimes.py
Log:
Updated to pybench 2.0.

See svn.python.org/external/pybench-2.0 for the original import of that
version.

Note that platform.py was not copied over from pybench-2.0 since
it is already part of Python 2.5.



Modified: python/trunk/Tools/pybench/Arithmetic.py
==============================================================================
--- python/trunk/Tools/pybench/Arithmetic.py	(original)
+++ python/trunk/Tools/pybench/Arithmetic.py	Tue Jun 13 20:56:56 2006
@@ -2,7 +2,7 @@
 
 class SimpleIntegerArithmetic(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5 * (3 + 5 + 5 + 3 + 3 + 3)
     rounds = 120000
 
@@ -157,9 +157,9 @@
 
 class SimpleFloatArithmetic(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5 * (3 + 5 + 5 + 3 + 3 + 3)
-    rounds = 100000
+    rounds = 120000
 
     def test(self):
 
@@ -312,7 +312,7 @@
 
 class SimpleIntFloatArithmetic(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5 * (3 + 5 + 5 + 3 + 3 + 3)
     rounds = 120000
 
@@ -468,9 +468,9 @@
 
 class SimpleLongArithmetic(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5 * (3 + 5 + 5 + 3 + 3 + 3)
-    rounds = 30000
+    rounds = 60000
 
     def test(self):
 
@@ -623,9 +623,9 @@
 
 class SimpleComplexArithmetic(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5 * (3 + 5 + 5 + 3 + 3 + 3)
-    rounds = 40000
+    rounds = 80000
 
     def test(self):
 

Modified: python/trunk/Tools/pybench/Calls.py
==============================================================================
--- python/trunk/Tools/pybench/Calls.py	(original)
+++ python/trunk/Tools/pybench/Calls.py	Tue Jun 13 20:56:56 2006
@@ -2,7 +2,7 @@
 
 class PythonFunctionCalls(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(1+4+4+2)
     rounds = 60000
 
@@ -111,9 +111,9 @@
 
 class BuiltinFunctionCalls(Test):
 
-    version = 0.4
+    version = 2.0
     operations = 5*(2+5+5+5)
-    rounds = 30000
+    rounds = 60000
 
     def test(self):
 
@@ -232,9 +232,9 @@
 
 class PythonMethodCalls(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(6 + 5 + 4)
-    rounds = 20000
+    rounds = 30000
 
     def test(self):
 
@@ -374,9 +374,9 @@
 
 class Recursion(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5
-    rounds = 50000
+    rounds = 100000
 
     def test(self):
 
@@ -407,3 +407,98 @@
 
         for i in xrange(self.rounds):
             pass
+
+
+### Test to make Fredrik happy...
+
+if __name__ == '__main__':
+    import timeit
+    if 0:
+        timeit.TestClass = PythonFunctionCalls
+        timeit.main(['-s', 'test = TestClass(); test.rounds = 1000',
+                     'test.test()'])
+    else:
+        setup = """\
+global f,f1,g,h
+
+# define functions
+def f():
+    pass
+
+def f1(x):
+    pass
+
+def g(a,b,c):
+    return a,b,c
+
+def h(a,b,c,d=1,e=2,f=3):
+    return d,e,f
+
+i = 1
+"""
+        test = """\
+f()
+f1(i)
+f1(i)
+f1(i)
+f1(i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+h(i,i,3,i,i)
+h(i,i,i,2,i,3)
+
+f()
+f1(i)
+f1(i)
+f1(i)
+f1(i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+h(i,i,3,i,i)
+h(i,i,i,2,i,3)
+
+f()
+f1(i)
+f1(i)
+f1(i)
+f1(i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+h(i,i,3,i,i)
+h(i,i,i,2,i,3)
+
+f()
+f1(i)
+f1(i)
+f1(i)
+f1(i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+h(i,i,3,i,i)
+h(i,i,i,2,i,3)
+
+f()
+f1(i)
+f1(i)
+f1(i)
+f1(i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+g(i,i,i)
+h(i,i,3,i,i)
+h(i,i,i,2,i,3)
+"""
+
+        timeit.main(['-s', setup,
+                     test])
+                     
+        

Modified: python/trunk/Tools/pybench/CommandLine.py
==============================================================================
--- python/trunk/Tools/pybench/CommandLine.py	(original)
+++ python/trunk/Tools/pybench/CommandLine.py	Tue Jun 13 20:56:56 2006
@@ -358,7 +358,7 @@
 
         except self.InternalError:
             print
-            print '* Internal Error'
+            print '* Internal Error (use --debug to display the traceback)'
             if self.debug:
                 print
                 traceback.print_exc(20, sys.stdout)

Modified: python/trunk/Tools/pybench/Constructs.py
==============================================================================
--- python/trunk/Tools/pybench/Constructs.py	(original)
+++ python/trunk/Tools/pybench/Constructs.py	Tue Jun 13 20:56:56 2006
@@ -2,7 +2,7 @@
 
 class IfThenElse(Test):
 
-    version = 0.31
+    version = 2.0
     operations = 30*3 # hard to say...
     rounds = 150000
 
@@ -469,9 +469,9 @@
 
 class NestedForLoops(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 1000*10*5
-    rounds = 150
+    rounds = 300
 
     def test(self):
 
@@ -494,9 +494,9 @@
 
 class ForLoops(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5 * 5
-    rounds = 8000
+    rounds = 10000
 
     def test(self):
 

Modified: python/trunk/Tools/pybench/Dict.py
==============================================================================
--- python/trunk/Tools/pybench/Dict.py	(original)
+++ python/trunk/Tools/pybench/Dict.py	Tue Jun 13 20:56:56 2006
@@ -2,9 +2,9 @@
 
 class DictCreation(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(5 + 5)
-    rounds = 60000
+    rounds = 80000
 
     def test(self):
 
@@ -77,7 +77,7 @@
 
 class DictWithStringKeys(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5*(6 + 6)
     rounds = 200000
 
@@ -166,9 +166,9 @@
 
 class DictWithFloatKeys(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5*(6 + 6)
-    rounds = 200000
+    rounds = 150000
 
     def test(self):
 
@@ -255,7 +255,7 @@
 
 class DictWithIntegerKeys(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5*(6 + 6)
     rounds = 200000
 
@@ -344,13 +344,14 @@
 
 class SimpleDictManipulation(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(6 + 6 + 6 + 6)
-    rounds = 50000
+    rounds = 100000
 
     def test(self):
 
         d = {}
+        has_key = d.has_key
 
         for i in xrange(self.rounds):
 
@@ -368,12 +369,12 @@
             x = d[4]
             x = d[5]
 
-            d.has_key(0)
-            d.has_key(2)
-            d.has_key(4)
-            d.has_key(6)
-            d.has_key(8)
-            d.has_key(10)
+            has_key(0)
+            has_key(2)
+            has_key(4)
+            has_key(6)
+            has_key(8)
+            has_key(10)
 
             del d[0]
             del d[1]
@@ -396,12 +397,12 @@
             x = d[4]
             x = d[5]
 
-            d.has_key(0)
-            d.has_key(2)
-            d.has_key(4)
-            d.has_key(6)
-            d.has_key(8)
-            d.has_key(10)
+            has_key(0)
+            has_key(2)
+            has_key(4)
+            has_key(6)
+            has_key(8)
+            has_key(10)
 
             del d[0]
             del d[1]
@@ -424,12 +425,12 @@
             x = d[4]
             x = d[5]
 
-            d.has_key(0)
-            d.has_key(2)
-            d.has_key(4)
-            d.has_key(6)
-            d.has_key(8)
-            d.has_key(10)
+            has_key(0)
+            has_key(2)
+            has_key(4)
+            has_key(6)
+            has_key(8)
+            has_key(10)
 
             del d[0]
             del d[1]
@@ -452,12 +453,12 @@
             x = d[4]
             x = d[5]
 
-            d.has_key(0)
-            d.has_key(2)
-            d.has_key(4)
-            d.has_key(6)
-            d.has_key(8)
-            d.has_key(10)
+            has_key(0)
+            has_key(2)
+            has_key(4)
+            has_key(6)
+            has_key(8)
+            has_key(10)
 
             del d[0]
             del d[1]
@@ -480,12 +481,12 @@
             x = d[4]
             x = d[5]
 
-            d.has_key(0)
-            d.has_key(2)
-            d.has_key(4)
-            d.has_key(6)
-            d.has_key(8)
-            d.has_key(10)
+            has_key(0)
+            has_key(2)
+            has_key(4)
+            has_key(6)
+            has_key(8)
+            has_key(10)
 
             del d[0]
             del d[1]
@@ -497,6 +498,7 @@
     def calibrate(self):
 
         d = {}
+        has_key = d.has_key
 
         for i in xrange(self.rounds):
             pass

Modified: python/trunk/Tools/pybench/Exceptions.py
==============================================================================
--- python/trunk/Tools/pybench/Exceptions.py	(original)
+++ python/trunk/Tools/pybench/Exceptions.py	Tue Jun 13 20:56:56 2006
@@ -2,9 +2,9 @@
 
 class TryRaiseExcept(Test):
 
-    version = 0.1
-    operations = 2 + 3
-    rounds = 60000
+    version = 2.0
+    operations = 2 + 3 + 3
+    rounds = 80000
 
     def test(self):
 
@@ -31,6 +31,18 @@
                 raise error,"something"
             except:
                 pass
+            try:
+                raise error("something")
+            except:
+                pass
+            try:
+                raise error("something")
+            except:
+                pass
+            try:
+                raise error("something")
+            except:
+                pass
 
     def calibrate(self):
 
@@ -42,9 +54,9 @@
 
 class TryExcept(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 15 * 10
-    rounds = 200000
+    rounds = 150000
 
     def test(self):
 
@@ -677,3 +689,11 @@
 
         for i in xrange(self.rounds):
             pass
+
+### Test to make Fredrik happy...
+
+if __name__ == '__main__':
+    import timeit
+    timeit.TestClass = TryRaiseExcept
+    timeit.main(['-s', 'test = TestClass(); test.rounds = 1000',
+                 'test.test()'])

Modified: python/trunk/Tools/pybench/Imports.py
==============================================================================
--- python/trunk/Tools/pybench/Imports.py	(original)
+++ python/trunk/Tools/pybench/Imports.py	Tue Jun 13 20:56:56 2006
@@ -6,9 +6,9 @@
 
 class SecondImport(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5 * 5
-    rounds = 20000
+    rounds = 40000
 
     def test(self):
 
@@ -51,9 +51,9 @@
 
 class SecondPackageImport(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5 * 5
-    rounds = 20000
+    rounds = 40000
 
     def test(self):
 
@@ -95,9 +95,9 @@
 
 class SecondSubmoduleImport(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5 * 5
-    rounds = 20000
+    rounds = 40000
 
     def test(self):
 

Modified: python/trunk/Tools/pybench/Instances.py
==============================================================================
--- python/trunk/Tools/pybench/Instances.py	(original)
+++ python/trunk/Tools/pybench/Instances.py	Tue Jun 13 20:56:56 2006
@@ -2,9 +2,9 @@
 
 class CreateInstances(Test):
 
-    version = 0.2
+    version = 2.0
     operations = 3 + 7 + 4
-    rounds = 60000
+    rounds = 80000
 
     def test(self):
 

Modified: python/trunk/Tools/pybench/Lists.py
==============================================================================
--- python/trunk/Tools/pybench/Lists.py	(original)
+++ python/trunk/Tools/pybench/Lists.py	Tue Jun 13 20:56:56 2006
@@ -2,22 +2,23 @@
 
 class SimpleListManipulation(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5* (6 + 6 + 6)
-    rounds = 60000
+    rounds = 130000
 
     def test(self):
 
         l = []
+        append = l.append
 
         for i in xrange(self.rounds):
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -33,12 +34,12 @@
             x = l[4]
             x = l[5]
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -54,12 +55,12 @@
             x = l[4]
             x = l[5]
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -75,12 +76,12 @@
             x = l[4]
             x = l[5]
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -96,12 +97,12 @@
             x = l[4]
             x = l[5]
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -124,15 +125,16 @@
     def calibrate(self):
 
         l = []
+        append = l.append
 
         for i in xrange(self.rounds):
             pass
 
 class ListSlicing(Test):
 
-    version = 0.4
+    version = 2.0
     operations = 25*(3+1+2+1)
-    rounds = 400
+    rounds = 800
 
     def test(self):
 
@@ -141,7 +143,7 @@
 
         for i in xrange(self.rounds):
 
-            l = range(100)
+            l = n[:]
 
             for j in r:
 
@@ -159,17 +161,14 @@
         r = range(25)
 
         for i in xrange(self.rounds):
-
-            l = range(100)
-
             for j in r:
                 pass
 
 class SmallLists(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(1+ 6 + 6 + 3 + 1)
-    rounds = 60000
+    rounds = 80000
 
     def test(self):
 
@@ -177,12 +176,13 @@
 
             l = []
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append = l.append
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -199,12 +199,13 @@
 
             l = []
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append = l.append
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -221,12 +222,13 @@
 
             l = []
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append = l.append
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -243,12 +245,13 @@
 
             l = []
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append = l.append
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -265,12 +268,13 @@
 
             l = []
 
-            l.append(2)
-            l.append(3)
-            l.append(4)
-            l.append(2)
-            l.append(3)
-            l.append(4)
+            append = l.append
+            append(2)
+            append(3)
+            append(4)
+            append(2)
+            append(3)
+            append(4)
 
             l[0] = 3
             l[1] = 4
@@ -288,4 +292,4 @@
     def calibrate(self):
 
         for i in xrange(self.rounds):
-            l = []
+            pass

Modified: python/trunk/Tools/pybench/Lookups.py
==============================================================================
--- python/trunk/Tools/pybench/Lookups.py	(original)
+++ python/trunk/Tools/pybench/Lookups.py	Tue Jun 13 20:56:56 2006
@@ -2,7 +2,7 @@
 
 class SpecialClassAttribute(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(12 + 12)
     rounds = 100000
 
@@ -183,7 +183,7 @@
 
 class NormalClassAttribute(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(12 + 12)
     rounds = 100000
 
@@ -369,7 +369,7 @@
 
 class SpecialInstanceAttribute(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(12 + 12)
     rounds = 100000
 
@@ -557,7 +557,7 @@
 
 class NormalInstanceAttribute(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(12 + 12)
     rounds = 100000
 
@@ -745,7 +745,7 @@
 
 class BuiltinMethodLookup(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(3*5 + 3*5)
     rounds = 70000
 

Modified: python/trunk/Tools/pybench/NewInstances.py
==============================================================================
--- python/trunk/Tools/pybench/NewInstances.py	(original)
+++ python/trunk/Tools/pybench/NewInstances.py	Tue Jun 13 20:56:56 2006
@@ -1,8 +1,17 @@
 from pybench import Test
 
+# Check for new-style class support:
+try:
+    class c(object):
+        pass
+except NameError:
+    raise ImportError
+
+###
+
 class CreateNewInstances(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 3 + 7 + 4
     rounds = 60000
 

Modified: python/trunk/Tools/pybench/Numbers.py
==============================================================================
--- python/trunk/Tools/pybench/Numbers.py	(original)
+++ python/trunk/Tools/pybench/Numbers.py	Tue Jun 13 20:56:56 2006
@@ -2,7 +2,7 @@
 
 class CompareIntegers(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 30 * 5
     rounds = 120000
 
@@ -198,9 +198,9 @@
 
 class CompareFloats(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 30 * 5
-    rounds = 60000
+    rounds = 80000
 
     def test(self):
 
@@ -394,7 +394,7 @@
 
 class CompareFloatsIntegers(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 30 * 5
     rounds = 60000
 
@@ -590,9 +590,9 @@
 
 class CompareLongs(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 30 * 5
-    rounds = 60000
+    rounds = 70000
 
     def test(self):
 

Modified: python/trunk/Tools/pybench/README
==============================================================================
--- python/trunk/Tools/pybench/README	(original)
+++ python/trunk/Tools/pybench/README	Tue Jun 13 20:56:56 2006
@@ -28,12 +28,37 @@
 Micro-Manual
 ------------
 
-Run 'pybench.py -h' to see the help screen.
-Run 'pybench.py' to just let the benchmark suite do it's thing and
-'pybench.py -f <file>' to have it store the results in a file too.
+Run 'pybench.py -h' to see the help screen.  Run 'pybench.py' to run
+the benchmark suite using default settings and 'pybench.py -f <file>'
+to have it store the results in a file too.
+
+It is usually a good idea to run pybench.py multiple times to see
+whether the environment, timers and benchmark run-times are suitable
+for doing benchmark tests. 
+
+You can use the comparison feature of pybench.py ('pybench.py -c
+<file>') to check how well the system behaves in comparison to a
+reference run. 
+
+If the differences are well below 10% for each test, then you have a
+system that is good for doing benchmark testings.  Of you get random
+differences of more than 10% or significant differences between the
+values for minimum and average time, then you likely have some
+background processes running which cause the readings to become
+inconsistent. Examples include: web-browsers, email clients, RSS
+readers, music players, backup programs, etc.
+
+If you are only interested in a few tests of the whole suite, you can
+use the filtering option, e.g. 'pybench.py -t string' will only
+run/show the tests that have 'string' in their name.
 
 This is the current output of pybench.py --help:
 
+"""
+------------------------------------------------------------------------
+PYBENCH - a benchmark test suite for Python interpreters/compilers.
+------------------------------------------------------------------------
+
 Synopsis:
  pybench.py [option] files...
 
@@ -42,14 +67,14 @@
   -f arg           save benchmark to file arg ()
   -c arg           compare benchmark with the one in file arg ()
   -s arg           show benchmark in file arg, then exit ()
-  -S               show statistics of benchmarks (0)
-  -w arg           set warp factor to arg (20)
-  -d               hide noise in compares (0)
-  --no-gc          disable garbage collection (0)
-  --no-syscheck    "disable" sys check interval (set to sys.maxint) (0)
-  -t arg           tests containing substring ()
-  -C arg           number of calibration runs (20)
-  -v               generate verbose output
+  -w arg           set warp factor to arg (10)
+  -t arg           run only tests with names matching arg ()
+  -C arg           set the number of calibration runs to arg (20)
+  -d               hide noise in comparisons (0)
+  -v               verbose output (not recommended) (0)
+  --with-gc        enable garbage collection (0)
+  --with-syscheck  use default sys check interval (0)
+  --timer arg      use given timer (time.time)
   -h               show this help text
   --help           show this help text
   --debug          enable debugging
@@ -57,17 +82,23 @@
   --examples       show examples of usage
 
 Version:
- 1.3
+ 2.0
 
 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.
 
-Examples:
+Available timers:
 
-python1.5 pybench.py -w 100 -f p15
-python1.4 pybench.py -w 100 -f p14
-python pybench.py -s p15 -c p14
+   time.time
+   time.clock
+   systimes.processtime
 
+Examples:
+
+python2.1 pybench.py -f p21.pybench
+python2.5 pybench.py -f p25.pybench
+python pybench.py -s p25.pybench -c p21.pybench
+"""
 
 License
 -------
@@ -78,184 +109,103 @@
 Sample output
 -------------
 
-PYBENCH 1.3
-
-Machine Details:
-   Platform ID:  Linux-2.6.8-24.19-default-x86_64-with-SuSE-9.2-x86-64
-   Executable:   /home/lemburg/projects/Python/Installation/bin/python
-   Python:       2.5a1.0
-   Compiler:     GCC 3.3.4 (pre 3.3.5 20040809)
-   Build:        Apr  9 2006 01:50:57 (#trunk)
-
-Searching for tests...
-   BuiltinFunctionCalls
-   BuiltinMethodLookup
-   CompareFloats
-   CompareFloatsIntegers
-   CompareIntegers
-   CompareInternedStrings
-   CompareLongs
-   CompareStrings
-   CompareUnicode
-   ConcatStrings
-   ConcatUnicode
-   CreateInstances
-   CreateStringsWithConcat
-   CreateUnicodeWithConcat
-   DictCreation
-   DictWithFloatKeys
-   DictWithIntegerKeys
-   DictWithStringKeys
-   ForLoops
-   IfThenElse
-   ListSlicing
-   NestedForLoops
-   NormalClassAttribute
-   NormalInstanceAttribute
-   PythonFunctionCalls
-   PythonMethodCalls
-   Recursion
-   SecondImport
-   SecondPackageImport
-   SecondSubmoduleImport
-   SimpleComplexArithmetic
-   SimpleDictManipulation
-   SimpleFloatArithmetic
-   SimpleIntFloatArithmetic
-   SimpleIntegerArithmetic
-   SimpleListManipulation
-   SimpleLongArithmetic
-   SmallLists
-   SmallTuples
-   SpecialClassAttribute
-   SpecialInstanceAttribute
-   StringMappings
-   StringPredicates
-   StringSlicing
-   TryExcept
-   TryRaiseExcept
-   TupleSlicing
-   UnicodeMappings
-   UnicodePredicates
-   UnicodeProperties
-   UnicodeSlicing
-
-Running 10 round(s) of the suite:
-
+"""
+-------------------------------------------------------------------------------
+PYBENCH 2.0
+-------------------------------------------------------------------------------
+* using Python 2.4.2
+* disabled garbage collection
+* system check interval set to maximum: 2147483647
+* using timer: time.time
+
+Calibrating tests. Please wait...
+
+Running 10 round(s) of the suite at warp factor 10:
+
+* Round 1 done in 6.388 seconds.
+* Round 2 done in 6.485 seconds.
+* Round 3 done in 6.786 seconds.
 ...
+* Round 10 done in 6.546 seconds.
 
- Round 10                         real   abs    overhead
-          BuiltinFunctionCalls:   0.030r 0.030a 0.000o
-           BuiltinMethodLookup:   0.059r 0.060a 0.001o
-                 CompareFloats:   0.050r 0.050a 0.000o
-         CompareFloatsIntegers:   0.050r 0.050a 0.000o
-               CompareIntegers:   0.070r 0.070a 0.000o
-        CompareInternedStrings:   0.039r 0.040a 0.001o
-                  CompareLongs:   0.050r 0.050a 0.000o
-                CompareStrings:   0.060r 0.060a 0.000o
-                CompareUnicode:   0.060r 0.060a 0.000o
-                 ConcatStrings:   0.040r 0.040a 0.000o
-                 ConcatUnicode:   0.050r 0.050a 0.000o
-               CreateInstances:   0.050r 0.050a 0.000o
-       CreateStringsWithConcat:   0.029r 0.030a 0.001o
-       CreateUnicodeWithConcat:   0.060r 0.060a 0.000o
-                  DictCreation:   0.040r 0.040a 0.000o
-             DictWithFloatKeys:   0.089r 0.090a 0.000o
-           DictWithIntegerKeys:   0.059r 0.060a 0.001o
-            DictWithStringKeys:   0.070r 0.070a 0.001o
-                      ForLoops:   0.050r 0.050a 0.000o
-                    IfThenElse:   0.070r 0.070a 0.000o
-                   ListSlicing:   0.030r 0.030a 0.000o
-                NestedForLoops:   0.030r 0.030a 0.000o
-          NormalClassAttribute:   0.060r 0.060a 0.000o
-       NormalInstanceAttribute:   0.060r 0.060a 0.000o
-           PythonFunctionCalls:   0.060r 0.060a 0.000o
-             PythonMethodCalls:   0.050r 0.050a 0.000o
-                     Recursion:   0.050r 0.050a 0.000o
-                  SecondImport:   0.030r 0.030a 0.000o
-           SecondPackageImport:   0.030r 0.030a 0.000o
-         SecondSubmoduleImport:   0.040r 0.040a 0.000o
-       SimpleComplexArithmetic:   0.030r 0.030a 0.000o
-        SimpleDictManipulation:   0.040r 0.040a 0.000o
-         SimpleFloatArithmetic:   0.050r 0.050a 0.001o
-      SimpleIntFloatArithmetic:   0.060r 0.060a 0.000o
-       SimpleIntegerArithmetic:   0.060r 0.060a 0.000o
-        SimpleListManipulation:   0.030r 0.030a 0.000o
-          SimpleLongArithmetic:   0.030r 0.030a 0.000o
-                    SmallLists:   0.050r 0.050a 0.000o
-                   SmallTuples:   0.050r 0.050a 0.000o
-         SpecialClassAttribute:   0.060r 0.060a 0.000o
-      SpecialInstanceAttribute:   0.079r 0.080a 0.001o
-                StringMappings:   0.060r 0.060a 0.000o
-              StringPredicates:   0.049r 0.050a 0.001o
-                 StringSlicing:   0.039r 0.040a 0.000o
-                     TryExcept:   0.079r 0.080a 0.001o
-                TryRaiseExcept:   0.059r 0.060a 0.001o
-                  TupleSlicing:   0.050r 0.050a 0.000o
-               UnicodeMappings:   0.070r 0.070a 0.001o
-             UnicodePredicates:   0.059r 0.060a 0.001o
-             UnicodeProperties:   0.059r 0.060a 0.001o
-                UnicodeSlicing:   0.050r 0.050a 0.000o
-                                 ----------------------
-            Average round time:      2.937 seconds
-
-
-Tests:                              per run    per oper.   overhead
-------------------------------------------------------------------------
-          BuiltinFunctionCalls:      29.85 ms    0.23 us    0.00 ms
-           BuiltinMethodLookup:      66.85 ms    0.13 us    0.50 ms
-                 CompareFloats:      43.00 ms    0.10 us    0.00 ms
-         CompareFloatsIntegers:      51.80 ms    0.12 us    0.00 ms
-               CompareIntegers:      70.70 ms    0.08 us    0.50 ms
-        CompareInternedStrings:      41.40 ms    0.08 us    0.50 ms
-                  CompareLongs:      47.90 ms    0.11 us    0.00 ms
-                CompareStrings:      58.50 ms    0.12 us    0.50 ms
-                CompareUnicode:      56.55 ms    0.15 us    0.50 ms
-                 ConcatStrings:      44.75 ms    0.30 us    0.00 ms
-                 ConcatUnicode:      54.55 ms    0.36 us    0.50 ms
-               CreateInstances:      50.95 ms    1.21 us    0.00 ms
-       CreateStringsWithConcat:      28.85 ms    0.14 us    0.50 ms
-       CreateUnicodeWithConcat:      53.75 ms    0.27 us    0.00 ms
-                  DictCreation:      41.90 ms    0.28 us    0.00 ms
-             DictWithFloatKeys:      88.50 ms    0.15 us    0.50 ms
-           DictWithIntegerKeys:      62.55 ms    0.10 us    0.50 ms
-            DictWithStringKeys:      60.50 ms    0.10 us    0.50 ms
-                      ForLoops:      46.90 ms    4.69 us    0.00 ms
-                    IfThenElse:      60.55 ms    0.09 us    0.00 ms
-                   ListSlicing:      29.90 ms    8.54 us    0.00 ms
-                NestedForLoops:      33.95 ms    0.10 us    0.00 ms
-          NormalClassAttribute:      62.75 ms    0.10 us    0.50 ms
-       NormalInstanceAttribute:      61.80 ms    0.10 us    0.50 ms
-           PythonFunctionCalls:      60.00 ms    0.36 us    0.00 ms
-             PythonMethodCalls:      50.00 ms    0.67 us    0.00 ms
-                     Recursion:      46.85 ms    3.75 us    0.00 ms
-                  SecondImport:      35.00 ms    1.40 us    0.00 ms
-           SecondPackageImport:      32.00 ms    1.28 us    0.00 ms
-         SecondSubmoduleImport:      38.00 ms    1.52 us    0.00 ms
-       SimpleComplexArithmetic:      26.85 ms    0.12 us    0.00 ms
-        SimpleDictManipulation:      40.85 ms    0.14 us    0.00 ms
-         SimpleFloatArithmetic:      48.70 ms    0.09 us    0.50 ms
-      SimpleIntFloatArithmetic:      57.70 ms    0.09 us    0.00 ms
-       SimpleIntegerArithmetic:      58.75 ms    0.09 us    0.50 ms
-        SimpleListManipulation:      34.80 ms    0.13 us    0.00 ms
-          SimpleLongArithmetic:      30.95 ms    0.19 us    0.50 ms
-                    SmallLists:      47.60 ms    0.19 us    0.00 ms
-                   SmallTuples:      48.80 ms    0.20 us    0.50 ms
-         SpecialClassAttribute:      61.70 ms    0.10 us    0.00 ms
-      SpecialInstanceAttribute:      76.70 ms    0.13 us    0.50 ms
-                StringMappings:      58.70 ms    0.47 us    0.00 ms
-              StringPredicates:      50.00 ms    0.18 us    1.00 ms
-                 StringSlicing:      39.65 ms    0.23 us    0.50 ms
-                     TryExcept:      84.45 ms    0.06 us    0.50 ms
-                TryRaiseExcept:      61.75 ms    4.12 us    0.50 ms
-                  TupleSlicing:      48.95 ms    0.47 us    0.00 ms
-               UnicodeMappings:      71.50 ms    3.97 us    0.50 ms
-             UnicodePredicates:      52.75 ms    0.23 us    1.00 ms
-             UnicodeProperties:      61.90 ms    0.31 us    1.00 ms
-                UnicodeSlicing:      53.75 ms    0.31 us    0.50 ms
-------------------------------------------------------------------------
-            Average round time:    2937.00 ms
-
+-------------------------------------------------------------------------------
+Benchmark: 2006-06-12 12:09:25
+-------------------------------------------------------------------------------
+
+    Rounds: 10
+    Warp:   10
+    Timer:  time.time
+
+    Machine Details:
+       Platform ID:  Linux-2.6.8-24.19-default-x86_64-with-SuSE-9.2-x86-64
+       Processor:    x86_64
+
+    Python:
+       Executable:   /usr/local/bin/python
+       Version:      2.4.2
+       Compiler:     GCC 3.3.4 (pre 3.3.5 20040809)
+       Bits:         64bit
+       Build:        Oct  1 2005 15:24:35 (#1)
+       Unicode:      UCS2
+
+
+Test                             minimum  average  operation  overhead
+-------------------------------------------------------------------------------
+          BuiltinFunctionCalls:    126ms    145ms    0.28us    0.274ms
+           BuiltinMethodLookup:    124ms    130ms    0.12us    0.316ms
+                 CompareFloats:    109ms    110ms    0.09us    0.361ms
+         CompareFloatsIntegers:    100ms    104ms    0.12us    0.271ms
+               CompareIntegers:    137ms    138ms    0.08us    0.542ms
+        CompareInternedStrings:    124ms    127ms    0.08us    1.367ms
+                  CompareLongs:    100ms    104ms    0.10us    0.316ms
+                CompareStrings:    111ms    115ms    0.12us    0.929ms
+                CompareUnicode:    108ms    128ms    0.17us    0.693ms
+                 ConcatStrings:    142ms    155ms    0.31us    0.562ms
+                 ConcatUnicode:    119ms    127ms    0.42us    0.384ms
+               CreateInstances:    123ms    128ms    1.14us    0.367ms
+            CreateNewInstances:    121ms    126ms    1.49us    0.335ms
+       CreateStringsWithConcat:    130ms    135ms    0.14us    0.916ms
+       CreateUnicodeWithConcat:    130ms    135ms    0.34us    0.361ms
+                  DictCreation:    108ms    109ms    0.27us    0.361ms
+             DictWithFloatKeys:    149ms    153ms    0.17us    0.678ms
+           DictWithIntegerKeys:    124ms    126ms    0.11us    0.915ms
+            DictWithStringKeys:    114ms    117ms    0.10us    0.905ms
+                      ForLoops:    110ms    111ms    4.46us    0.063ms
+                    IfThenElse:    118ms    119ms    0.09us    0.685ms
+                   ListSlicing:    116ms    120ms    8.59us    0.103ms
+                NestedForLoops:    125ms    137ms    0.09us    0.019ms
+          NormalClassAttribute:    124ms    136ms    0.11us    0.457ms
+       NormalInstanceAttribute:    110ms    117ms    0.10us    0.454ms
+           PythonFunctionCalls:    107ms    113ms    0.34us    0.271ms
+             PythonMethodCalls:    140ms    149ms    0.66us    0.141ms
+                     Recursion:    156ms    166ms    3.32us    0.452ms
+                  SecondImport:    112ms    118ms    1.18us    0.180ms
+           SecondPackageImport:    118ms    127ms    1.27us    0.180ms
+         SecondSubmoduleImport:    140ms    151ms    1.51us    0.180ms
+       SimpleComplexArithmetic:    128ms    139ms    0.16us    0.361ms
+        SimpleDictManipulation:    134ms    136ms    0.11us    0.452ms
+         SimpleFloatArithmetic:    110ms    113ms    0.09us    0.571ms
+      SimpleIntFloatArithmetic:    106ms    111ms    0.08us    0.548ms
+       SimpleIntegerArithmetic:    106ms    109ms    0.08us    0.544ms
+        SimpleListManipulation:    103ms    113ms    0.10us    0.587ms
+          SimpleLongArithmetic:    112ms    118ms    0.18us    0.271ms
+                    SmallLists:    105ms    116ms    0.17us    0.366ms
+                   SmallTuples:    108ms    128ms    0.24us    0.406ms
+         SpecialClassAttribute:    119ms    136ms    0.11us    0.453ms
+      SpecialInstanceAttribute:    143ms    155ms    0.13us    0.454ms
+                StringMappings:    115ms    121ms    0.48us    0.405ms
+              StringPredicates:    120ms    129ms    0.18us    2.064ms
+                 StringSlicing:    111ms    127ms    0.23us    0.781ms
+                     TryExcept:    125ms    126ms    0.06us    0.681ms
+                TryRaiseExcept:    133ms    137ms    2.14us    0.361ms
+                  TupleSlicing:    117ms    120ms    0.46us    0.066ms
+               UnicodeMappings:    156ms    160ms    4.44us    0.429ms
+             UnicodePredicates:    117ms    121ms    0.22us    2.487ms
+             UnicodeProperties:    115ms    153ms    0.38us    2.070ms
+                UnicodeSlicing:    126ms    129ms    0.26us    0.689ms
+-------------------------------------------------------------------------------
+Totals:                           6283ms   6673ms
+"""
 ________________________________________________________________________
 
 Writing New Tests
@@ -293,7 +243,7 @@
 
     # 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.
+    # 1-2 seconds (at warp 1).
     rounds = 100000
 
     def test(self):
@@ -377,6 +327,41 @@
 variable should be updated. Therefafter, comparisons with previous
 versions of the test will list as "n/a" to reflect the change.
 
+
+Version History
+---------------
+
+  2.0: rewrote parts of pybench which resulted in more repeatable
+       timings:
+        - made timer a parameter
+        - changed the platform default timer to use high-resolution
+          timers rather than process timers (which have a much lower
+          resolution)
+        - added option to select timer
+        - added process time timer (using systimes.py)
+        - changed to use min() as timing estimator (average
+          is still taken as well to provide an idea of the difference)
+        - garbage collection is turned off per default
+        - sys check interval is set to the highest possible value
+        - calibration is now a separate step and done using
+          a different strategy that allows measuring the test
+          overhead more accurately
+        - modified the tests to each give a run-time of between
+          100-200ms using warp 10
+        - changed default warp factor to 10 (from 20)
+        - compared results with timeit.py and confirmed measurements
+        - bumped all test versions to 2.0
+        - updated platform.py to the latest version
+        - changed the output format a bit to make it look
+          nicer
+        - refactored the APIs somewhat
+  1.3+: Steve Holden added the NewInstances test and the filtering 
+       option during the NeedForSpeed sprint; this also triggered a long 
+       discussion on how to improve benchmark timing and finally
+       resulted in the release of 2.0
+  1.3: initial checkin into the Python SVN repository
+
+
 Have fun,
 --
 Marc-Andre Lemburg

Modified: python/trunk/Tools/pybench/Setup.py
==============================================================================
--- python/trunk/Tools/pybench/Setup.py	(original)
+++ python/trunk/Tools/pybench/Setup.py	Tue Jun 13 20:56:56 2006
@@ -14,7 +14,7 @@
 
 # Defaults
 Number_of_rounds = 10
-Warp_factor = 20
+Warp_factor = 10
 
 # Import tests
 from Arithmetic import *
@@ -24,8 +24,8 @@
 from Instances import *
 try:
     from NewInstances import *
-except:
-    print "Cannot test new-style objects"
+except ImportError:
+    pass
 from Lists import *
 from Tuples import *
 from Dict import *

Modified: python/trunk/Tools/pybench/Strings.py
==============================================================================
--- python/trunk/Tools/pybench/Strings.py	(original)
+++ python/trunk/Tools/pybench/Strings.py	Tue Jun 13 20:56:56 2006
@@ -3,9 +3,9 @@
 
 class ConcatStrings(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 10 * 5
-    rounds = 60000
+    rounds = 100000
 
     def test(self):
 
@@ -85,7 +85,7 @@
 
 class CompareStrings(Test):
 
-    version = 0.2
+    version = 2.0
     operations = 10 * 5
     rounds = 200000
 
@@ -167,9 +167,9 @@
 
 class CompareInternedStrings(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 10 * 5
-    rounds = 200000
+    rounds = 300000
 
     def test(self):
 
@@ -249,9 +249,9 @@
 
 class CreateStringsWithConcat(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 10 * 5
-    rounds = 80000
+    rounds = 200000
 
     def test(self):
 
@@ -324,9 +324,9 @@
 
 class StringSlicing(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5 * 7
-    rounds = 100000
+    rounds = 160000
 
     def test(self):
 
@@ -387,7 +387,7 @@
 
     class StringMappings(Test):
 
-        version = 0.1
+        version = 2.0
         operations = 3 * (5 + 4 + 2 + 1)
         rounds = 70000
 
@@ -460,9 +460,9 @@
 
     class StringPredicates(Test):
 
-        version = 0.1
+        version = 2.0
         operations = 10 * 7
-        rounds = 80000
+        rounds = 100000
 
         def test(self):
 

Modified: python/trunk/Tools/pybench/Tuples.py
==============================================================================
--- python/trunk/Tools/pybench/Tuples.py	(original)
+++ python/trunk/Tools/pybench/Tuples.py	Tue Jun 13 20:56:56 2006
@@ -2,18 +2,17 @@
 
 class TupleSlicing(Test):
 
-    version = 0.31
+    version = 2.0
     operations = 3 * 25 * 10 * 7
-    rounds = 400
+    rounds = 500
 
     def test(self):
 
         r = range(25)
+        t = tuple(range(100))
 
         for i in xrange(self.rounds):
 
-            t = tuple(range(100))
-
             for j in r:
 
                 m = t[50:]
@@ -259,20 +258,17 @@
     def calibrate(self):
 
         r = range(25)
+        t = tuple(range(100))
 
         for i in xrange(self.rounds):
-
-            t = tuple(range(100))
-
             for j in r:
-
                 pass
 
 class SmallTuples(Test):
 
-    version = 0.3
+    version = 2.0
     operations = 5*(1 + 3 + 6 + 2)
-    rounds = 80000
+    rounds = 90000
 
     def test(self):
 

Modified: python/trunk/Tools/pybench/Unicode.py
==============================================================================
--- python/trunk/Tools/pybench/Unicode.py	(original)
+++ python/trunk/Tools/pybench/Unicode.py	Tue Jun 13 20:56:56 2006
@@ -8,7 +8,7 @@
 
 class ConcatUnicode(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 10 * 5
     rounds = 60000
 
@@ -90,7 +90,7 @@
 
 class CompareUnicode(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 10 * 5
     rounds = 150000
 
@@ -172,7 +172,7 @@
 
 class CreateUnicodeWithConcat(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 10 * 5
     rounds = 80000
 
@@ -247,9 +247,9 @@
 
 class UnicodeSlicing(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5 * 7
-    rounds = 100000
+    rounds = 140000
 
     def test(self):
 
@@ -308,7 +308,7 @@
 
 class UnicodeMappings(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 3 * (5 + 4 + 2 + 1)
     rounds = 10000
 
@@ -381,9 +381,9 @@
 
 class UnicodePredicates(Test):
 
-    version = 0.1
+    version = 2.0
     operations = 5 * 9
-    rounds = 100000
+    rounds = 120000
 
     def test(self):
 
@@ -458,7 +458,7 @@
 else:
     class UnicodeProperties(Test):
 
-        version = 0.1
+        version = 2.0
         operations = 5 * 8
         rounds = 100000
 

Added: python/trunk/Tools/pybench/clockres.py
==============================================================================
--- (empty file)
+++ python/trunk/Tools/pybench/clockres.py	Tue Jun 13 20:56:56 2006
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+""" clockres - calculates the resolution in seconds of a given timer.
+
+    Copyright (c) 2006, Marc-Andre Lemburg (mal at egenix.com). See the
+    documentation for further information on copyrights, or contact
+    the author. All Rights Reserved.
+
+"""
+import time
+
+TEST_TIME = 1.0
+
+def clockres(timer):
+    d = {}
+    wallclock = time.time
+    start = wallclock()
+    stop = wallclock() + TEST_TIME
+    spin_loops = range(1000)
+    while 1:
+        now = wallclock()
+        if now >= stop:
+           break
+        for i in spin_loops:
+            d[timer()] = 1
+    values = d.keys()
+    values.sort()
+    min_diff = TEST_TIME
+    for i in range(len(values) - 1):
+        diff = values[i+1] - values[i]
+        if diff < min_diff:
+            min_diff = diff
+    return min_diff
+
+if __name__ == '__main__':
+    print 'Clock resolution of various timer implementations:'    
+    print 'time.clock:           %10.3fus' % (clockres(time.clock) * 1e6)
+    print 'time.time:            %10.3fus' % (clockres(time.time) * 1e6)
+    try:
+        import systimes
+        print 'systimes.processtime: %10.3fus' % (clockres(systimes.processtime) * 1e6)
+    except ImportError:
+        pass
+

Modified: python/trunk/Tools/pybench/pybench.py
==============================================================================
--- python/trunk/Tools/pybench/pybench.py	(original)
+++ python/trunk/Tools/pybench/pybench.py	Tue Jun 13 20:56:56 2006
@@ -34,20 +34,7 @@
 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 @@
 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 @@
     # 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 @@
 
     # 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
+
+    # Number of calibration runs to use
+    calibration_runs = CALIBRATION_RUNS
+
+    # List of calibration timings
+    overhead_times = None
+
+    # List of test run timings
+    times = []
 
-    def __init__(self,warp=1):
+    # Timer used for the benchmark
+    timer = TIMER_PLATFORM_DEFAULT
 
-        if warp > 1:
-            self.rounds = self.rounds / warp
+    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 @@
 
         """
         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 @@
             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 @@
 
 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
-            for t in l:
-                print '  ', t
-            print len(l), "tests found"
+
+    def calibrate(self):
+
+        print 'Calibrating tests. Please wait...'
+        if self.verbose:
+            print
+            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):
+    def stat(self):
+
+        """ Return benchmark run statistics as tuple:
 
-        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)
+            (minimum round time,
+             average round time,
+             maximum round time)
 
-        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 = ''
+            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 @@
 
     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,37 +812,51 @@
         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 '-' * LINE
+        print 'PYBENCH %s' % __version__
+        print '-' * LINE
+        print '* using Python %s' % (string.split(sys.version)[0])
 
-        print 'PYBENCH',__version__
-
-        # 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'
 
-        print
+        # "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
 
-        if not compare_to:
-            print_machine()
-            print
+        print
 
         if compare_to:
             try:
@@ -460,8 +865,10 @@
                 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 @@
                 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:

Modified: python/trunk/Tools/pybench/systimes.py
==============================================================================
--- python/trunk/Tools/pybench/systimes.py	(original)
+++ python/trunk/Tools/pybench/systimes.py	Tue Jun 13 20:56:56 2006
@@ -16,7 +16,7 @@
     platforms.
 
     If no supported timing methods based on process time can be found,
-    the module reverts to the highest resolution wall-time timer
+    the module reverts to the highest resolution wall-clock timer
     instead. The system time part will then always be 0.0.
 
     The module exports one public API:
@@ -52,8 +52,8 @@
 USE_WIN32PROCESS_GETPROCESSTIMES = 'win32process.GetProcessTimes()'
 USE_RESOURCE_GETRUSAGE = 'resource.getrusage()'
 USE_PROCESS_TIME_CLOCK = 'time.clock() (process time)'
-USE_WALL_TIME_CLOCK = 'time.clock() (wall-time)'
-USE_WALL_TIME_TIME = 'time.time() (wall-time)'
+USE_WALL_TIME_CLOCK = 'time.clock() (wall-clock)'
+USE_WALL_TIME_TIME = 'time.time() (wall-clock)'
 
 if sys.platform[:3] == 'win':
     # Windows platform
@@ -63,7 +63,7 @@
         try:
             import ctypes
         except ImportError:
-            # Use the wall-time implementation time.clock(), since this
+            # Use the wall-clock implementation time.clock(), since this
             # is the highest resolution clock available on Windows
             SYSTIMES_IMPLEMENTATION = USE_WALL_TIME_CLOCK
         else:
@@ -91,7 +91,7 @@
         # time)
         SYSTIMES_IMPLEMENTATION = USE_PROCESS_TIME_CLOCK
     else:
-        # Use wall-time implementation time.time() since this provides
+        # Use wall-clock implementation time.time() since this provides
         # the highest resolution clock on most systems
         SYSTIMES_IMPLEMENTATION = USE_WALL_TIME_TIME
 
@@ -103,24 +103,27 @@
 def process_time_clock_systimes():
     return (time.clock(), 0.0)
 
-def wall_time_clock_systimes():
+def wall_clock_clock_systimes():
     return (time.clock(), 0.0)
 
-def wall_time_time_systimes():
+def wall_clock_time_systimes():
     return (time.time(), 0.0)
 
 # Number of clock ticks per second for the values returned
 # by GetProcessTimes() on Windows.
 #
-# Note: Ticks returned by GetProcessTimes() are micro-seconds on
-# Windows XP (though the docs say 100ns intervals)
-WIN32_PROCESS_TIMES_TICKS_PER_SECOND = 10e6
+# Note: Ticks returned by GetProcessTimes() are 100ns intervals on
+# Windows XP. However, the process times are only updated with every
+# clock tick and the frequency of these is somewhat lower: depending
+# on the OS version between 10ms and 15ms. Even worse, the process
+# time seems to be allocated to process currently running when the
+# clock interrupt arrives, ie. it is possible that the current time
+# slice gets accounted to a different process.
+
+WIN32_PROCESS_TIMES_TICKS_PER_SECOND = 1e7
 
 def win32process_getprocesstimes_systimes():
     d = win32process.GetProcessTimes(win32process.GetCurrentProcess())
-    # Note: I'm not sure whether KernelTime on Windows is the same as
-    # system time on Unix - I've yet to see a non-zero value for
-    # KernelTime on Windows.
     return (d['UserTime'] / WIN32_PROCESS_TIMES_TICKS_PER_SECOND,
             d['KernelTime'] / WIN32_PROCESS_TIMES_TICKS_PER_SECOND)
 
@@ -149,10 +152,10 @@
     systimes = process_time_clock_systimes
 
 elif SYSTIMES_IMPLEMENTATION is USE_WALL_TIME_CLOCK:
-    systimes = wall_time_clock_systimes
+    systimes = wall_clock_clock_systimes
 
 elif SYSTIMES_IMPLEMENTATION is USE_WALL_TIME_TIME:
-    systimes = wall_time_time_systimes
+    systimes = wall_clock_time_systimes
 
 elif SYSTIMES_IMPLEMENTATION is USE_WIN32PROCESS_GETPROCESSTIMES:
     systimes = win32process_getprocesstimes_systimes
@@ -163,6 +166,17 @@
 else:
     raise TypeError('no suitable systimes() implementation found')
 
+def processtime():
+
+    """ Return the total time spent on the process.
+
+        This is the sum of user and system time as returned by
+        systimes().
+
+    """
+    user, system = systimes()
+    return user + system
+
 ### Testing
 
 def some_workload():


More information about the Python-checkins mailing list