Welcome, guest | Sign In | My Account | Store | Cart

This recipe explains how to check that a given function does not run slower than a given pystone rate. It first calculates the pystone ratio on your box.

Python, 57 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# by Tarek Ziadé

# let's use pystone instead of seconds here
# (from Stephan Richter idea)
from test import pystone
import time

# TOLERANCE in Pystones
kPS = 1000
TOLERANCE = 0.5*kPS 

class DurationError(AssertionError): pass

def local_pystone():
    return pystone.pystones(loops=pystone.LOOPS)

def timedtest(max_num_pystones, current_pystone=local_pystone()):
    """ decorator timedtest """
    if not isinstance(max_num_pystones, float):
        max_num_pystones = float(max_num_pystones)

    def _timedtest(function):
        def wrapper(*args, **kw):
            start_time = time.time()
            try:
                return function(*args, **kw)
            finally:
                total_time = time.time() - start_time
                if total_time == 0:
                    pystone_total_time = 0
                else:
                    pystone_rate = current_pystone[0] / current_pystone[1]
                    pystone_total_time = total_time / pystone_rate
                if pystone_total_time > (max_num_pystones + TOLERANCE):
                    raise DurationError((('Test too long (%.2f Ps, '
                                        'need at most %.2f Ps)')
                                        % (pystone_total_time,
                                            max_num_pystones)))
        return wrapper

    return _timedtest

This decorator is not to use in production code, and would rather
 fit in functional or unit tests. This make performance tests portable to any box and fits performance regression tests you would want to run in unit tests.

For example, in this test we want to be sure test_critical() does not last more than 2kPS:

  >>> import unittest
  >>> class MesTests(unittest.TestCase):
  ...     @timedtest(2*kPS)
  ...     def test_critical(self):
  ...         a =''
  ...         for i in range(50000):
  ...             a = a + 'x' * 200
  >>> suite = unittest.makeSuite(MesTests)
  >>> unittest.TextTestRunner().run(suite)
  <unittest._TextTestResult run=1 errors=0 failures=0>

DurationError is an AssertionError so it is reported as a failure in tests.

local_pystone() is separated so you can call it once for many tests,and give it in the second argument of the decorator.

TOLERANCE is used to prevent failures for a busy CPU. (it's faster than using a 3-times test like hotshot does)

Created by Tarek Ziadé on Wed, 12 Oct 2005 (PSF)
Python recipes (4591)
Tarek Ziadé's recipes (2)

Required Modules

  • (none specified)

Other Information and Tasks