2

I want to time some code that depends on some setup. The setup code looks a little like this:

>>> b = range(1, 1001)

And the code I want to time looks vaguely like this:

>>> sorted(b)

Except my code uses a different function than sorted. But that ain't important right now.

Anyhow, I know how to time this code as long as I pass in strings to timeit:

>>> import timeit
>>> t = timeit.Timer("sorted(b)", "b = range(1, 1001)")
>>> min(t.repeat(3, 100))

How do I use a setup callable and have it put stuff into the namespace that the stmt callable can access?

In other words, how do I do the same thing as the code above, but with callables, and not strings containing callables?

Incidentally, the bigger goal here is to reuse code from my unit tests to measure performance:

import unittest
class TestSorting(unittest.TestCase):

    def setUp(self):
        self.b = range(1, 1001)

    def test_sorted(self):
        sorted(self.b)

I expect to do a little work. The timeit setup will need to make an instance of TestSorting and somehow the stmt code will have to use that particular instance.

Once I understand how to have the timeit setup put stuff into the same namespace as the timeit stmt, I'll look into how to translate unittest.TestCase instances into something that can feed right into timeit.Timer.

Matt

2 Answers2

1

In 2.6, you can "just do it", per the docs:

Changed in version 2.6: The stmt and setup parameters can now also take objects that are callable without arguments. This will embed calls to them in a timer function that will then be executed by timeit(). Note that the timing overhead is a little larger in this case because of the extra function calls.

Are you stuck using an old version of Python? In that case, I would suggest taking Python 2.6's timer.py sources and "backporting" them to the version you're stuck with -- should not be difficult if that version is 2.5 (gets harder the farther you need to travel back in time, of course). I would generally not recommend this for "production" code, but it's quite fine for code that supports testing, debugging, performance measurements, and the like.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 1
    Sorry, I should have made my problem more clear. I'm complaining that I don't know how to have setup callable create something in the namespace used by the stmt code. In the strings example, the setup string creates an object "b" that is visible in the statement code. I'm baffled how to do the same thing with callables. I don't know how to have the setup callable create that "b" object so that the stmt callable can see it. – W. Matthew Wilson May 12 '10 at 14:53
  • This post http://stackoverflow.com/questions/1966750/measure-time-of-a-function-with-arguments-in-python shows an example of the setup statement adding stuff to the namespace used by the statement code. I'm taking the problem a little bit further, and trying to do this all with callables, rather than strings. – W. Matthew Wilson May 12 '10 at 15:24
  • Is there a way to use callables with arguments? – endolith Oct 03 '13 at 23:57
  • @endolith: you can use a callable like `lambda: foo(56)` but it adds some overhead – endolith Oct 04 '13 at 14:52
1

I've asked a variant of this question here and also got an answer that didn't solve my problem. I believe we are both failing to articulate the problem in terms Pythonic.

I don't know if this is a hack, workaround, or how it is supposed to be done, but it does work:

>>> import timeit
>>> b = range(1, 1001)
>>> t = timeit.Timer('sorted(b)', setup='from __main__ import b')
>>> t.repeat(3, 100)
[0.0024309158325195312, 0.0024671554565429688, 0.0024020671844482422]
# was it really running 'sorted(b)'? let's compare'
>>> t = timeit.Timer('pass', setup='from __main__ import b')
>>> t.repeat(3, 100) 
[3.0994415283203125e-06, 2.1457672119140625e-06, 1.9073486328125e-06] 
# teeny times for 'pass', 'sorted(b)' took more time

I have read timeit.py, it works by constructing a statement and then calls eval() on it using new namespaces which have (seemingly) no connection to your main namespace. Note that since sorted is a built-in, the eval'ed expression inside timeit.repeat() has access to the name; if it was your def you'd have to from __main__ import b, myfunc.

I hope there is more proper way than this to achieve the end.

msw
  • 42,753
  • 9
  • 87
  • 112