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

Module timeit requires you to specify the number of iterations for the timing loop. This module does not.

Python, 78 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""
Determine function execution time.

 >>> def f():
 ... return sum(range(10))
 ...
 >>> pytime(f)
 (Time to execute function f, including function call overhead).
 >>> 1.0/pytime(f)
 (Function calls/sec, including function call overhead).
 >>> 1.0/pytime_statement('sum(range(10))')
 (Statements/sec, does not include any function call overhead).

"""

import sys

# Source code is public domain.
if sys.platform == "win32":
  from time import clock
else:
  from time import time as clock

def pytime(f, args=(), kwargs={}, Tmax=2.0):
  """
  Calls f many times to determine the average time to execute f.

  Tmax is the maximum time to spend in pytime(), in seconds.
  """
  count = 1
  while True:
    start = clock()
    if args == () and kwargs == {}:
      for i in xrange(count):
        f()
    elif kwargs == {}:
      for i in xrange(count):
        f(*args)
    else:
      for i in xrange(count):
        f(*args, **kwargs)
    T = clock() - start
    if T >= Tmax/4.0: break
    count *= 2
  return T / count


def pytime_statement(stmt, global_dict=None, Tmax=2.0,
                    repeat_count=128):
  """
  Determine time to execute statement (or block) of Python code.

  Here global_dict is the globals dict used for exec, Tmax is the max
  time to spend in pytime_statement(), in sec, and repeat_count is the
  number of times to paste stmt into the inner timing loop (this is
  automatically set to 1 if stmt takes too long).
  """
  if global_dict is None:
    global_dict = globals()

  ns = {}
  code = 'def timed_func():' + ('\n' +
         '\n'.join(['  '+x for x in stmt.split('\n')]))
  exec code in global_dict, ns

  start = clock()
  ns['timed_func']()
  T = clock() - start

  if T >= Tmax/4.0:
    return T
  elif T >= Tmax/4.0/repeat_count:
    return pytime(ns['timed_func'], (), {}, Tmax-T)
  else:
    code = 'def timed_func():' + ('\n' +
           '\n'.join(['  '+x for x in stmt.split('\n')]))*repeat_count
    exec code in global_dict, ns
    return pytime(ns['timed_func'], (), {}, Tmax-T) / repeat_count

Python's timing facilities are...unpythonic. Module timeit requires you to specify the number of iterations for the timing loop. But that number varies according to the speed of the machine you're running on, level of optimization of your code, etc. It's more Pythonic to calculate the number of iterations in the timing procedure. This recipe does just that!

2 comments

S W 18 years, 6 months ago  # | flag

this function will report incorrect results. The code...

start = clock()
for i in range(count):
  f(*args, **kwargs)
end = clock()

...is calculating the duration of 1) a for loop, 2) a list creation call (range), and 3) a function call. It is not calculating the time a function takes to run its code.

Connelly Barnes (author) 18 years, 6 months ago  # | flag

Addressing concerns. Thanks for criticising the pytime() function. As part of the recipe's design, I intended pytime() to include the function call overhead. However, as you said, using range() in the timing loop is a bad idea (I modified the recipe to use xrange). Also, I reduced some more overhead by calling f() directly if no arguments are given, f(args) if only positional arguments are given, and f(args, **kwargs) at last resort.

Now back to your concerns. Often one may want to time Python statements directly, without the function call overhead. I added pytime_statement(), which does this. It works by pasting the statement multiple times into the inner timing loop, thus it has slightly less timing overhead than module timeit.

Created by Connelly Barnes on Tue, 4 Oct 2005 (PSF)
Python recipes (4591)
Connelly Barnes's recipes (7)

Required Modules

Other Information and Tasks