105

I'm trying to find out how much time it takes to execute a Python statement, so I looked online and found that the standard library provides a module called timeit that purports to do exactly that:

import timeit

def foo():
    # ... contains code I want to time ...

def dotime():
    t = timeit.Timer("foo()")
    time = t.timeit(1)
    print "took %fs\n" % (time,)

dotime()

However, this produces an error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in dotime
  File "/usr/local/lib/python2.6/timeit.py", line 193, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
NameError: global name 'foo' is not defined

I'm still new to Python and I don't fully understand all the scoping issues it has, but I don't know why this snippet doesn't work. Any thoughts?

Kyle Cronin
  • 77,653
  • 43
  • 148
  • 164

5 Answers5

102

Change this line:

t = timeit.Timer("foo()")

To this:

t = timeit.Timer("foo()", "from __main__ import foo")

Check out the link you provided at the very bottom.

To give the timeit module access to functions you define, you can pass a setup parameter which contains an import statement:

I just tested it on my machine and it worked with the changes.

Melebius
  • 6,183
  • 4
  • 39
  • 52
Paolo Bergantino
  • 480,997
  • 81
  • 517
  • 436
  • 32
    It works! However, this is a pretty stupid interface design if I have to both supply the command I wish to time as a string and to import the __main__ module for it to work. – Kyle Cronin Feb 15 '09 at 23:21
  • 3
    Python namespacing is utter madness to me. I assume that it makes sense to a certain sort of mind, but that sort of mind isn't one I happen to posess. Thank $DEITY for Ruby, in my case. – womble Feb 16 '09 at 06:03
  • 5
    womble, this is a wart, not a general python namespace problem. Main thread: http://writeonly.wordpress.com/2008/09/12/using-python-timeit-to-time-functions/ has links to other discussions about this. – Gregg Lind Feb 18 '09 at 23:12
  • 1
    @Gregg The link is no longer accessible (error 404). What was in that discussion? – ovgolovin Sep 13 '11 at 22:29
  • ovgovolin: looks fine to me! http://writeonly.wordpress.com/2008/09/12/using-python-timeit-to-time-functions/ with links: http://mail.python.org/pipermail/python-list/2006-November/412340.html and http://mail.python.org/pipermail/python-list/2006-November/412341.html – Gregg Lind Sep 14 '11 at 21:09
  • 1
    ov, those were outdated. now repaired: http://mail.python.org/pipermail/python-list/2006-November/463664.html – Gregg Lind Sep 14 '11 at 21:14
  • @Gregg Thanks! Please, the next time put `@` before the name (`ovgolovin` or others) when reply. Otherwise the user is not notified (e.g. I wasn't notified when you made the replies in this thread). – ovgolovin Sep 15 '11 at 13:00
  • 2
    those links are all dead – endolith Aug 11 '13 at 17:33
  • 4
    For Python 3, use the solution by @user2314737 `t = timeit.Timer("foo()", globals=globals()) ` – Nyxynyx Sep 04 '20 at 16:55
44

With Python 3, you can use globals=globals()

t = timeit.Timer("foo()", globals=globals())

From the documentation:

Another option is to pass globals() to the globals parameter, which will cause the code to be executed within your current global namespace. This can be more convenient than individually specifying imports

Richard Dally
  • 1,432
  • 2
  • 21
  • 38
user2314737
  • 27,088
  • 20
  • 102
  • 114
  • 4
    Works for Python 3 only. `globals` not a parameter for Python 2's `timeit` – tony_tiger Oct 30 '17 at 06:25
  • 1
    I had to merge `globals` and `locals` like this: `imports_and_vars=globals(); imports_and_vars.update(locals())` then pass it like `t = timeit.Timer("foo()", globals=imports_and_vars)` – nmz787 Apr 07 '21 at 21:58
22

You can try this hack:

import timeit

def foo():
    print 'bar'

def dotime():
    t = timeit.Timer("foo()")
    time = t.timeit(1)
    print "took %fs\n" % (time,)

import __builtin__
__builtin__.__dict__.update(locals())

dotime()
Luka Zakrajšek
  • 1,079
  • 12
  • 12
  • 2
    This hack is great if you'd otherwise need complex setup code. – Luís Marques Jul 03 '11 at 23:19
  • Better than the startup code alternative given in other replies (i.e. better than `t = timeit.Timer("foo()", "from __main__ import foo")`). Specially if you want to test several different functions, it will save a lot of typing! – A.Sommerh Jul 28 '14 at 07:30
  • 1
    I've got about 20 imports, so passing them as an argument gets messy quick. This hack is awesome! – kramer65 Dec 24 '14 at 08:08
  • 8
    Great! On python3 however you need `import builtins` and 'builtins.__dict__.update(locals())' – greole Feb 24 '16 at 07:46
  • Can you time multiple functions in the Time() function? – edo101 Jul 27 '20 at 14:12
8
t = timeit.Timer("foo()", "from __main__ import foo")

Since timeit doesn't have your stuff in scope.

dwc
  • 24,196
  • 7
  • 44
  • 55
0

add into your setup "import thisfile; "

then when you call the setup function myfunc() use "thisfile.myfunc()"

eg "thisfile.py"

def myfunc():

 return 5

def testable(par):

 pass



t=timeit.timeit(stmt="testable(v)",setup="import thisfile; v=thisfile.myfunc();").repeat(10)

print( t )
BenMorel
  • 34,448
  • 50
  • 182
  • 322
mist42nz
  • 97
  • 1
  • 8