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