Update to 2.7.3
[profile/ivi/python.git] / Tools / pybench / pybench.py
1 #!/usr/local/bin/python -O
2
3 """ A Python Benchmark Suite
4
5 """
6 #
7 # Note: Please keep this module compatible to Python 1.5.2.
8 #
9 # Tests may include features in later Python versions, but these
10 # should then be embedded in try-except clauses in the configuration
11 # module Setup.py.
12 #
13
14 # pybench Copyright
15 __copyright__ = """\
16 Copyright (c), 1997-2006, Marc-Andre Lemburg (mal@lemburg.com)
17 Copyright (c), 2000-2006, eGenix.com Software GmbH (info@egenix.com)
18
19                    All Rights Reserved.
20
21 Permission to use, copy, modify, and distribute this software and its
22 documentation for any purpose and without fee or royalty is hereby
23 granted, provided that the above copyright notice appear in all copies
24 and that both that copyright notice and this permission notice appear
25 in supporting documentation or portions thereof, including
26 modifications, that you make.
27
28 THE AUTHOR MARC-ANDRE LEMBURG DISCLAIMS ALL WARRANTIES WITH REGARD TO
29 THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
30 FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
31 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
32 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
33 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
34 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !
35 """
36
37 import sys, time, operator, string, platform
38 from CommandLine import *
39
40 try:
41     import cPickle
42     pickle = cPickle
43 except ImportError:
44     import pickle
45
46 # Version number; version history: see README file !
47 __version__ = '2.0'
48
49 ### Constants
50
51 # Second fractions
52 MILLI_SECONDS = 1e3
53 MICRO_SECONDS = 1e6
54
55 # Percent unit
56 PERCENT = 100
57
58 # Horizontal line length
59 LINE = 79
60
61 # Minimum test run-time
62 MIN_TEST_RUNTIME = 1e-3
63
64 # Number of calibration runs to use for calibrating the tests
65 CALIBRATION_RUNS = 20
66
67 # Number of calibration loops to run for each calibration run
68 CALIBRATION_LOOPS = 20
69
70 # Allow skipping calibration ?
71 ALLOW_SKIPPING_CALIBRATION = 1
72
73 # Timer types
74 TIMER_TIME_TIME = 'time.time'
75 TIMER_TIME_CLOCK = 'time.clock'
76 TIMER_SYSTIMES_PROCESSTIME = 'systimes.processtime'
77
78 # Choose platform default timer
79 if sys.platform[:3] == 'win':
80     # On WinXP this has 2.5ms resolution
81     TIMER_PLATFORM_DEFAULT = TIMER_TIME_CLOCK
82 else:
83     # On Linux this has 1ms resolution
84     TIMER_PLATFORM_DEFAULT = TIMER_TIME_TIME
85
86 # Print debug information ?
87 _debug = 0
88
89 ### Helpers
90
91 def get_timer(timertype):
92
93     if timertype == TIMER_TIME_TIME:
94         return time.time
95     elif timertype == TIMER_TIME_CLOCK:
96         return time.clock
97     elif timertype == TIMER_SYSTIMES_PROCESSTIME:
98         import systimes
99         return systimes.processtime
100     else:
101         raise TypeError('unknown timer type: %s' % timertype)
102
103 def get_machine_details():
104
105     if _debug:
106         print 'Getting machine details...'
107     buildno, builddate = platform.python_build()
108     python = platform.python_version()
109     try:
110         unichr(100000)
111     except ValueError:
112         # UCS2 build (standard)
113         unicode = 'UCS2'
114     except NameError:
115         unicode = None
116     else:
117         # UCS4 build (most recent Linux distros)
118         unicode = 'UCS4'
119     bits, linkage = platform.architecture()
120     return {
121         'platform': platform.platform(),
122         'processor': platform.processor(),
123         'executable': sys.executable,
124         'implementation': getattr(platform, 'python_implementation',
125                                   lambda:'n/a')(),
126         'python': platform.python_version(),
127         'compiler': platform.python_compiler(),
128         'buildno': buildno,
129         'builddate': builddate,
130         'unicode': unicode,
131         'bits': bits,
132         }
133
134 def print_machine_details(d, indent=''):
135
136     l = ['Machine Details:',
137          '   Platform ID:    %s' % d.get('platform', 'n/a'),
138          '   Processor:      %s' % d.get('processor', 'n/a'),
139          '',
140          'Python:',
141          '   Implementation: %s' % d.get('implementation', 'n/a'),
142          '   Executable:     %s' % d.get('executable', 'n/a'),
143          '   Version:        %s' % d.get('python', 'n/a'),
144          '   Compiler:       %s' % d.get('compiler', 'n/a'),
145          '   Bits:           %s' % d.get('bits', 'n/a'),
146          '   Build:          %s (#%s)' % (d.get('builddate', 'n/a'),
147                                           d.get('buildno', 'n/a')),
148          '   Unicode:        %s' % d.get('unicode', 'n/a'),
149          ]
150     print indent + string.join(l, '\n' + indent) + '\n'
151
152 ### Test baseclass
153
154 class Test:
155
156     """ All test must have this class as baseclass. It provides
157         the necessary interface to the benchmark machinery.
158
159         The tests must set .rounds to a value high enough to let the
160         test run between 20-50 seconds. This is needed because
161         clock()-timing only gives rather inaccurate values (on Linux,
162         for example, it is accurate to a few hundreths of a
163         second). If you don't want to wait that long, use a warp
164         factor larger than 1.
165
166         It is also important to set the .operations variable to a
167         value representing the number of "virtual operations" done per
168         call of .run().
169
170         If you change a test in some way, don't forget to increase
171         its version number.
172
173     """
174
175     ### Instance variables that each test should override
176
177     # Version number of the test as float (x.yy); this is important
178     # for comparisons of benchmark runs - tests with unequal version
179     # number will not get compared.
180     version = 2.0
181
182     # The number of abstract operations done in each round of the
183     # test. An operation is the basic unit of what you want to
184     # measure. The benchmark will output the amount of run-time per
185     # operation. Note that in order to raise the measured timings
186     # significantly above noise level, it is often required to repeat
187     # sets of operations more than once per test round. The measured
188     # overhead per test round should be less than 1 second.
189     operations = 1
190
191     # Number of rounds to execute per test run. This should be
192     # adjusted to a figure that results in a test run-time of between
193     # 1-2 seconds.
194     rounds = 100000
195
196     ### Internal variables
197
198     # Mark this class as implementing a test
199     is_a_test = 1
200
201     # Last timing: (real, run, overhead)
202     last_timing = (0.0, 0.0, 0.0)
203
204     # Warp factor to use for this test
205     warp = 1
206
207     # Number of calibration runs to use
208     calibration_runs = CALIBRATION_RUNS
209
210     # List of calibration timings
211     overhead_times = None
212
213     # List of test run timings
214     times = []
215
216     # Timer used for the benchmark
217     timer = TIMER_PLATFORM_DEFAULT
218
219     def __init__(self, warp=None, calibration_runs=None, timer=None):
220
221         # Set parameters
222         if warp is not None:
223             self.rounds = int(self.rounds / warp)
224             if self.rounds == 0:
225                 raise ValueError('warp factor set too high')
226             self.warp = warp
227         if calibration_runs is not None:
228             if (not ALLOW_SKIPPING_CALIBRATION and
229                 calibration_runs < 1):
230                 raise ValueError('at least one calibration run is required')
231             self.calibration_runs = calibration_runs
232         if timer is not None:
233             self.timer = timer
234
235         # Init variables
236         self.times = []
237         self.overhead_times = []
238
239         # We want these to be in the instance dict, so that pickle
240         # saves them
241         self.version = self.version
242         self.operations = self.operations
243         self.rounds = self.rounds
244
245     def get_timer(self):
246
247         """ Return the timer function to use for the test.
248
249         """
250         return get_timer(self.timer)
251
252     def compatible(self, other):
253
254         """ Return 1/0 depending on whether the test is compatible
255             with the other Test instance or not.
256
257         """
258         if self.version != other.version:
259             return 0
260         if self.rounds != other.rounds:
261             return 0
262         return 1
263
264     def calibrate_test(self):
265
266         if self.calibration_runs == 0:
267             self.overhead_times = [0.0]
268             return
269
270         calibrate = self.calibrate
271         timer = self.get_timer()
272         calibration_loops = range(CALIBRATION_LOOPS)
273
274         # Time the calibration loop overhead
275         prep_times = []
276         for i in range(self.calibration_runs):
277             t = timer()
278             for i in calibration_loops:
279                 pass
280             t = timer() - t
281             prep_times.append(t / CALIBRATION_LOOPS)
282         min_prep_time = min(prep_times)
283         if _debug:
284             print
285             print 'Calib. prep time     = %.6fms' % (
286                 min_prep_time * MILLI_SECONDS)
287
288         # Time the calibration runs (doing CALIBRATION_LOOPS loops of
289         # .calibrate() method calls each)
290         for i in range(self.calibration_runs):
291             t = timer()
292             for i in calibration_loops:
293                 calibrate()
294             t = timer() - t
295             self.overhead_times.append(t / CALIBRATION_LOOPS
296                                        - min_prep_time)
297
298         # Check the measured times
299         min_overhead = min(self.overhead_times)
300         max_overhead = max(self.overhead_times)
301         if _debug:
302             print 'Calib. overhead time = %.6fms' % (
303                 min_overhead * MILLI_SECONDS)
304         if min_overhead < 0.0:
305             raise ValueError('calibration setup did not work')
306         if max_overhead - min_overhead > 0.1:
307             raise ValueError(
308                 'overhead calibration timing range too inaccurate: '
309                 '%r - %r' % (min_overhead, max_overhead))
310
311     def run(self):
312
313         """ Run the test in two phases: first calibrate, then
314             do the actual test. Be careful to keep the calibration
315             timing low w/r to the test timing.
316
317         """
318         test = self.test
319         timer = self.get_timer()
320
321         # Get calibration
322         min_overhead = min(self.overhead_times)
323
324         # Test run
325         t = timer()
326         test()
327         t = timer() - t
328         if t < MIN_TEST_RUNTIME:
329             raise ValueError('warp factor too high: '
330                              'test times are < 10ms')
331         eff_time = t - min_overhead
332         if eff_time < 0:
333             raise ValueError('wrong calibration')
334         self.last_timing = (eff_time, t, min_overhead)
335         self.times.append(eff_time)
336
337     def calibrate(self):
338
339         """ Calibrate the test.
340
341             This method should execute everything that is needed to
342             setup and run the test - except for the actual operations
343             that you intend to measure. pybench uses this method to
344             measure the test implementation overhead.
345
346         """
347         return
348
349     def test(self):
350
351         """ Run the test.
352
353             The test needs to run self.rounds executing
354             self.operations number of operations each.
355
356         """
357         return
358
359     def stat(self):
360
361         """ Return test run statistics as tuple:
362
363             (minimum run time,
364              average run time,
365              total run time,
366              average time per operation,
367              minimum overhead time)
368
369         """
370         runs = len(self.times)
371         if runs == 0:
372             return 0.0, 0.0, 0.0, 0.0
373         min_time = min(self.times)
374         total_time = reduce(operator.add, self.times, 0.0)
375         avg_time = total_time / float(runs)
376         operation_avg = total_time / float(runs
377                                            * self.rounds
378                                            * self.operations)
379         if self.overhead_times:
380             min_overhead = min(self.overhead_times)
381         else:
382             min_overhead = self.last_timing[2]
383         return min_time, avg_time, total_time, operation_avg, min_overhead
384
385 ### Load Setup
386
387 # This has to be done after the definition of the Test class, since
388 # the Setup module will import subclasses using this class.
389
390 import Setup
391
392 ### Benchmark base class
393
394 class Benchmark:
395
396     # Name of the benchmark
397     name = ''
398
399     # Number of benchmark rounds to run
400     rounds = 1
401
402     # Warp factor use to run the tests
403     warp = 1                    # Warp factor
404
405     # Average benchmark round time
406     roundtime = 0
407
408     # Benchmark version number as float x.yy
409     version = 2.0
410
411     # Produce verbose output ?
412     verbose = 0
413
414     # Dictionary with the machine details
415     machine_details = None
416
417     # Timer used for the benchmark
418     timer = TIMER_PLATFORM_DEFAULT
419
420     def __init__(self, name, verbose=None, timer=None, warp=None,
421                  calibration_runs=None):
422
423         if name:
424             self.name = name
425         else:
426             self.name = '%04i-%02i-%02i %02i:%02i:%02i' % \
427                         (time.localtime(time.time())[:6])
428         if verbose is not None:
429             self.verbose = verbose
430         if timer is not None:
431             self.timer = timer
432         if warp is not None:
433             self.warp = warp
434         if calibration_runs is not None:
435             self.calibration_runs = calibration_runs
436
437         # Init vars
438         self.tests = {}
439         if _debug:
440             print 'Getting machine details...'
441         self.machine_details = get_machine_details()
442
443         # Make .version an instance attribute to have it saved in the
444         # Benchmark pickle
445         self.version = self.version
446
447     def get_timer(self):
448
449         """ Return the timer function to use for the test.
450
451         """
452         return get_timer(self.timer)
453
454     def compatible(self, other):
455
456         """ Return 1/0 depending on whether the benchmark is
457             compatible with the other Benchmark instance or not.
458
459         """
460         if self.version != other.version:
461             return 0
462         if (self.machine_details == other.machine_details and
463             self.timer != other.timer):
464             return 0
465         if (self.calibration_runs == 0 and
466             other.calibration_runs != 0):
467             return 0
468         if (self.calibration_runs != 0 and
469             other.calibration_runs == 0):
470             return 0
471         return 1
472
473     def load_tests(self, setupmod, limitnames=None):
474
475         # Add tests
476         if self.verbose:
477             print 'Searching for tests ...'
478             print '--------------------------------------'
479         for testclass in setupmod.__dict__.values():
480             if not hasattr(testclass, 'is_a_test'):
481                 continue
482             name = testclass.__name__
483             if  name == 'Test':
484                 continue
485             if (limitnames is not None and
486                 limitnames.search(name) is None):
487                 continue
488             self.tests[name] = testclass(
489                 warp=self.warp,
490                 calibration_runs=self.calibration_runs,
491                 timer=self.timer)
492         l = self.tests.keys()
493         l.sort()
494         if self.verbose:
495             for name in l:
496                 print '  %s' % name
497             print '--------------------------------------'
498             print '  %i tests found' % len(l)
499             print
500
501     def calibrate(self):
502
503         print 'Calibrating tests. Please wait...',
504         sys.stdout.flush()
505         if self.verbose:
506             print
507             print
508             print 'Test                              min      max'
509             print '-' * LINE
510         tests = self.tests.items()
511         tests.sort()
512         for i in range(len(tests)):
513             name, test = tests[i]
514             test.calibrate_test()
515             if self.verbose:
516                 print '%30s:  %6.3fms  %6.3fms' % \
517                       (name,
518                        min(test.overhead_times) * MILLI_SECONDS,
519                        max(test.overhead_times) * MILLI_SECONDS)
520         if self.verbose:
521             print
522             print 'Done with the calibration.'
523         else:
524             print 'done.'
525         print
526
527     def run(self):
528
529         tests = self.tests.items()
530         tests.sort()
531         timer = self.get_timer()
532         print 'Running %i round(s) of the suite at warp factor %i:' % \
533               (self.rounds, self.warp)
534         print
535         self.roundtimes = []
536         for i in range(self.rounds):
537             if self.verbose:
538                 print ' Round %-25i  effective   absolute  overhead' % (i+1)
539             total_eff_time = 0.0
540             for j in range(len(tests)):
541                 name, test = tests[j]
542                 if self.verbose:
543                     print '%30s:' % name,
544                 test.run()
545                 (eff_time, abs_time, min_overhead) = test.last_timing
546                 total_eff_time = total_eff_time + eff_time
547                 if self.verbose:
548                     print '    %5.0fms    %5.0fms %7.3fms' % \
549                           (eff_time * MILLI_SECONDS,
550                            abs_time * MILLI_SECONDS,
551                            min_overhead * MILLI_SECONDS)
552             self.roundtimes.append(total_eff_time)
553             if self.verbose:
554                 print ('                   '
555                        '               ------------------------------')
556                 print ('                   '
557                        '     Totals:    %6.0fms' %
558                        (total_eff_time * MILLI_SECONDS))
559                 print
560             else:
561                 print '* Round %i done in %.3f seconds.' % (i+1,
562                                                             total_eff_time)
563         print
564
565     def stat(self):
566
567         """ Return benchmark run statistics as tuple:
568
569             (minimum round time,
570              average round time,
571              maximum round time)
572
573             XXX Currently not used, since the benchmark does test
574                 statistics across all rounds.
575
576         """
577         runs = len(self.roundtimes)
578         if runs == 0:
579             return 0.0, 0.0
580         min_time = min(self.roundtimes)
581         total_time = reduce(operator.add, self.roundtimes, 0.0)
582         avg_time = total_time / float(runs)
583         max_time = max(self.roundtimes)
584         return (min_time, avg_time, max_time)
585
586     def print_header(self, title='Benchmark'):
587
588         print '-' * LINE
589         print '%s: %s' % (title, self.name)
590         print '-' * LINE
591         print
592         print '    Rounds: %s' % self.rounds
593         print '    Warp:   %s' % self.warp
594         print '    Timer:  %s' % self.timer
595         print
596         if self.machine_details:
597             print_machine_details(self.machine_details, indent='    ')
598             print
599
600     def print_benchmark(self, hidenoise=0, limitnames=None):
601
602         print ('Test                          '
603                '   minimum  average  operation  overhead')
604         print '-' * LINE
605         tests = self.tests.items()
606         tests.sort()
607         total_min_time = 0.0
608         total_avg_time = 0.0
609         for name, test in tests:
610             if (limitnames is not None and
611                 limitnames.search(name) is None):
612                 continue
613             (min_time,
614              avg_time,
615              total_time,
616              op_avg,
617              min_overhead) = test.stat()
618             total_min_time = total_min_time + min_time
619             total_avg_time = total_avg_time + avg_time
620             print '%30s:  %5.0fms  %5.0fms  %6.2fus  %7.3fms' % \
621                   (name,
622                    min_time * MILLI_SECONDS,
623                    avg_time * MILLI_SECONDS,
624                    op_avg * MICRO_SECONDS,
625                    min_overhead *MILLI_SECONDS)
626         print '-' * LINE
627         print ('Totals:                        '
628                ' %6.0fms %6.0fms' %
629                (total_min_time * MILLI_SECONDS,
630                 total_avg_time * MILLI_SECONDS,
631                 ))
632         print
633
634     def print_comparison(self, compare_to, hidenoise=0, limitnames=None):
635
636         # Check benchmark versions
637         if compare_to.version != self.version:
638             print ('* Benchmark versions differ: '
639                    'cannot compare this benchmark to "%s" !' %
640                    compare_to.name)
641             print
642             self.print_benchmark(hidenoise=hidenoise,
643                                  limitnames=limitnames)
644             return
645
646         # Print header
647         compare_to.print_header('Comparing with')
648         print ('Test                          '
649                '   minimum run-time        average  run-time')
650         print ('                              '
651                '   this    other   diff    this    other   diff')
652         print '-' * LINE
653
654         # Print test comparisons
655         tests = self.tests.items()
656         tests.sort()
657         total_min_time = other_total_min_time = 0.0
658         total_avg_time = other_total_avg_time = 0.0
659         benchmarks_compatible = self.compatible(compare_to)
660         tests_compatible = 1
661         for name, test in tests:
662             if (limitnames is not None and
663                 limitnames.search(name) is None):
664                 continue
665             (min_time,
666              avg_time,
667              total_time,
668              op_avg,
669              min_overhead) = test.stat()
670             total_min_time = total_min_time + min_time
671             total_avg_time = total_avg_time + avg_time
672             try:
673                 other = compare_to.tests[name]
674             except KeyError:
675                 other = None
676             if other is None:
677                 # Other benchmark doesn't include the given test
678                 min_diff, avg_diff = 'n/a', 'n/a'
679                 other_min_time = 0.0
680                 other_avg_time = 0.0
681                 tests_compatible = 0
682             else:
683                 (other_min_time,
684                  other_avg_time,
685                  other_total_time,
686                  other_op_avg,
687                  other_min_overhead) = other.stat()
688                 other_total_min_time = other_total_min_time + other_min_time
689                 other_total_avg_time = other_total_avg_time + other_avg_time
690                 if (benchmarks_compatible and
691                     test.compatible(other)):
692                     # Both benchmark and tests are comparable
693                     min_diff = ((min_time * self.warp) /
694                                 (other_min_time * other.warp) - 1.0)
695                     avg_diff = ((avg_time * self.warp) /
696                                 (other_avg_time * other.warp) - 1.0)
697                     if hidenoise and abs(min_diff) < 10.0:
698                         min_diff = ''
699                     else:
700                         min_diff = '%+5.1f%%' % (min_diff * PERCENT)
701                     if hidenoise and abs(avg_diff) < 10.0:
702                         avg_diff = ''
703                     else:
704                         avg_diff = '%+5.1f%%' % (avg_diff * PERCENT)
705                 else:
706                     # Benchmark or tests are not comparable
707                     min_diff, avg_diff = 'n/a', 'n/a'
708                     tests_compatible = 0
709             print '%30s: %5.0fms %5.0fms %7s %5.0fms %5.0fms %7s' % \
710                   (name,
711                    min_time * MILLI_SECONDS,
712                    other_min_time * MILLI_SECONDS * compare_to.warp / self.warp,
713                    min_diff,
714                    avg_time * MILLI_SECONDS,
715                    other_avg_time * MILLI_SECONDS * compare_to.warp / self.warp,
716                    avg_diff)
717         print '-' * LINE
718
719         # Summarise test results
720         if not benchmarks_compatible or not tests_compatible:
721             min_diff, avg_diff = 'n/a', 'n/a'
722         else:
723             if other_total_min_time != 0.0:
724                 min_diff = '%+5.1f%%' % (
725                     ((total_min_time * self.warp) /
726                      (other_total_min_time * compare_to.warp) - 1.0) * PERCENT)
727             else:
728                 min_diff = 'n/a'
729             if other_total_avg_time != 0.0:
730                 avg_diff = '%+5.1f%%' % (
731                     ((total_avg_time * self.warp) /
732                      (other_total_avg_time * compare_to.warp) - 1.0) * PERCENT)
733             else:
734                 avg_diff = 'n/a'
735         print ('Totals:                       '
736                '  %5.0fms %5.0fms %7s %5.0fms %5.0fms %7s' %
737                (total_min_time * MILLI_SECONDS,
738                 (other_total_min_time * compare_to.warp/self.warp
739                  * MILLI_SECONDS),
740                 min_diff,
741                 total_avg_time * MILLI_SECONDS,
742                 (other_total_avg_time * compare_to.warp/self.warp
743                  * MILLI_SECONDS),
744                 avg_diff
745                ))
746         print
747         print '(this=%s, other=%s)' % (self.name,
748                                        compare_to.name)
749         print
750
751 class PyBenchCmdline(Application):
752
753     header = ("PYBENCH - a benchmark test suite for Python "
754               "interpreters/compilers.")
755
756     version = __version__
757
758     debug = _debug
759
760     options = [ArgumentOption('-n',
761                               'number of rounds',
762                               Setup.Number_of_rounds),
763                ArgumentOption('-f',
764                               'save benchmark to file arg',
765                               ''),
766                ArgumentOption('-c',
767                               'compare benchmark with the one in file arg',
768                               ''),
769                ArgumentOption('-s',
770                               'show benchmark in file arg, then exit',
771                               ''),
772                ArgumentOption('-w',
773                               'set warp factor to arg',
774                               Setup.Warp_factor),
775                ArgumentOption('-t',
776                               'run only tests with names matching arg',
777                               ''),
778                ArgumentOption('-C',
779                               'set the number of calibration runs to arg',
780                               CALIBRATION_RUNS),
781                SwitchOption('-d',
782                             'hide noise in comparisons',
783                             0),
784                SwitchOption('-v',
785                             'verbose output (not recommended)',
786                             0),
787                SwitchOption('--with-gc',
788                             'enable garbage collection',
789                             0),
790                SwitchOption('--with-syscheck',
791                             'use default sys check interval',
792                             0),
793                ArgumentOption('--timer',
794                             'use given timer',
795                             TIMER_PLATFORM_DEFAULT),
796                ]
797
798     about = """\
799 The normal operation is to run the suite and display the
800 results. Use -f to save them for later reuse or comparisons.
801
802 Available timers:
803
804    time.time
805    time.clock
806    systimes.processtime
807
808 Examples:
809
810 python2.1 pybench.py -f p21.pybench
811 python2.5 pybench.py -f p25.pybench
812 python pybench.py -s p25.pybench -c p21.pybench
813 """
814     copyright = __copyright__
815
816     def main(self):
817
818         rounds = self.values['-n']
819         reportfile = self.values['-f']
820         show_bench = self.values['-s']
821         compare_to = self.values['-c']
822         hidenoise = self.values['-d']
823         warp = int(self.values['-w'])
824         withgc = self.values['--with-gc']
825         limitnames = self.values['-t']
826         if limitnames:
827             if _debug:
828                 print '* limiting test names to one with substring "%s"' % \
829                       limitnames
830             limitnames = re.compile(limitnames, re.I)
831         else:
832             limitnames = None
833         verbose = self.verbose
834         withsyscheck = self.values['--with-syscheck']
835         calibration_runs = self.values['-C']
836         timer = self.values['--timer']
837
838         print '-' * LINE
839         print 'PYBENCH %s' % __version__
840         print '-' * LINE
841         print '* using %s %s' % (
842             getattr(platform, 'python_implementation', lambda:'Python')(),
843             string.join(string.split(sys.version), ' '))
844
845         # Switch off garbage collection
846         if not withgc:
847             try:
848                 import gc
849             except ImportError:
850                 print '* Python version doesn\'t support garbage collection'
851             else:
852                 try:
853                     gc.disable()
854                 except NotImplementedError:
855                     print '* Python version doesn\'t support gc.disable'
856                 else:
857                     print '* disabled garbage collection'
858
859         # "Disable" sys check interval
860         if not withsyscheck:
861             # Too bad the check interval uses an int instead of a long...
862             value = 2147483647
863             try:
864                 sys.setcheckinterval(value)
865             except (AttributeError, NotImplementedError):
866                 print '* Python version doesn\'t support sys.setcheckinterval'
867             else:
868                 print '* system check interval set to maximum: %s' % value
869
870         if timer == TIMER_SYSTIMES_PROCESSTIME:
871             import systimes
872             print '* using timer: systimes.processtime (%s)' % \
873                   systimes.SYSTIMES_IMPLEMENTATION
874         else:
875             print '* using timer: %s' % timer
876
877         print
878
879         if compare_to:
880             try:
881                 f = open(compare_to,'rb')
882                 bench = pickle.load(f)
883                 bench.name = compare_to
884                 f.close()
885                 compare_to = bench
886             except IOError, reason:
887                 print '* Error opening/reading file %s: %s' % (
888                     repr(compare_to),
889                     reason)
890                 compare_to = None
891
892         if show_bench:
893             try:
894                 f = open(show_bench,'rb')
895                 bench = pickle.load(f)
896                 bench.name = show_bench
897                 f.close()
898                 bench.print_header()
899                 if compare_to:
900                     bench.print_comparison(compare_to,
901                                            hidenoise=hidenoise,
902                                            limitnames=limitnames)
903                 else:
904                     bench.print_benchmark(hidenoise=hidenoise,
905                                           limitnames=limitnames)
906             except IOError, reason:
907                 print '* Error opening/reading file %s: %s' % (
908                     repr(show_bench),
909                     reason)
910                 print
911             return
912
913         if reportfile:
914             print 'Creating benchmark: %s (rounds=%i, warp=%i)' % \
915                   (reportfile, rounds, warp)
916             print
917
918         # Create benchmark object
919         bench = Benchmark(reportfile,
920                           verbose=verbose,
921                           timer=timer,
922                           warp=warp,
923                           calibration_runs=calibration_runs)
924         bench.rounds = rounds
925         bench.load_tests(Setup, limitnames=limitnames)
926         try:
927             bench.calibrate()
928             bench.run()
929         except KeyboardInterrupt:
930             print
931             print '*** KeyboardInterrupt -- Aborting'
932             print
933             return
934         bench.print_header()
935         if compare_to:
936             bench.print_comparison(compare_to,
937                                    hidenoise=hidenoise,
938                                    limitnames=limitnames)
939         else:
940             bench.print_benchmark(hidenoise=hidenoise,
941                                   limitnames=limitnames)
942
943         # Ring bell
944         sys.stderr.write('\007')
945
946         if reportfile:
947             try:
948                 f = open(reportfile,'wb')
949                 bench.name = reportfile
950                 pickle.dump(bench,f)
951                 f.close()
952             except IOError, reason:
953                 print '* Error opening/writing reportfile'
954             except IOError, reason:
955                 print '* Error opening/writing reportfile %s: %s' % (
956                     reportfile,
957                     reason)
958                 print
959
960 if __name__ == '__main__':
961     PyBenchCmdline()