timeit module: am I missing something obvious?

Discussion in 'Python' started by Steven D'Aprano, Feb 20, 2006.

  1. When using the timeit module, you pass the code you
    want to time as strings:

    import timeit
    t = timeit.Timer("foo(x, y)", \
    """from module import foo
    x = 27
    y = 45
    """)
    elapsed_time = t.timeit()

    This is all very well, but it feels quite unnatural to
    me. Why am I passing strings around when functions are
    first class objects? Have I missed something obvious?

    I understand that sometimes you have to pass strings,
    because statements are NOT objects in Python. But what
    about when your code doesn't use statements?

    It seems to me it would be really useful to be able to
    do something like this:

    # this doesn't work...
    import timeit
    from module import foo
    x = 27
    y = 45
    t = timeit.Timer(foo, args=[x, y])
    elapsed_time = t.timeit()

    instead of messing about with setup strings and the like.

    Am I missing something obvious? Does this functionality
    already exist? Is there a reason why it can't exist?



    --
    Steven.
     
    Steven D'Aprano, Feb 20, 2006
    #1
    1. Advertising

  2. Steven D'Aprano

    Peter Otten Guest

    Steven D'Aprano wrote:

    > When using the timeit module, you pass the code you
    > want to time as strings:
    >
    > import timeit
    > t = timeit.Timer("foo(x, y)", \
    > """from module import foo
    > x = 27
    > y = 45
    > """)
    > elapsed_time = t.timeit()
    >
    > This is all very well, but it feels quite unnatural to
    > me. Why am I passing strings around when functions are
    > first class objects? Have I missed something obvious?


    You are supposed to time small pieces of code where a function call would be
    a significant overhead.

    > I understand that sometimes you have to pass strings,
    > because statements are NOT objects in Python. But what
    > about when your code doesn't use statements?
    >
    > It seems to me it would be really useful to be able to
    > do something like this:
    >
    > # this doesn't work...
    > import timeit
    > from module import foo
    > x = 27
    > y = 45
    > t = timeit.Timer(foo, args=[x, y])
    > elapsed_time = t.timeit()
    >
    > instead of messing about with setup strings and the like.
    >
    > Am I missing something obvious? Does this functionality
    > already exist? Is there a reason why it can't exist?


    Just do it :)

    I would suggest a slight modification:

    t = timeit.Timer.for_function(foo, x, y, a=..., b=...)

    And while you are at it you could also move the code that dynamically
    adjusts the number of repetitions from the main() function into a Timer
    method and make its invocation the default for the timeit() method. (If
    that has not been done already, see
    http://mail.python.org/pipermail/python-dev/2006-January/059952.html)

    Peter
     
    Peter Otten, Feb 20, 2006
    #2
    1. Advertising

  3. Peter Otten <> writes:

    > Steven D'Aprano wrote:
    >
    >> When using the timeit module, you pass the code you
    >> want to time as strings:

    ....
    >> This is all very well, but it feels quite unnatural to
    >> me. Why am I passing strings around when functions are
    >> first class objects? Have I missed something obvious?

    >
    > You are supposed to time small pieces of code where a function call would be
    > a significant overhead.


    I think it can be useful to time longer code as well.

    Moreover, when you pass in strings, you have to ensure that
    the namespace is set-up correctly, which can be awkward at
    times. If you could pass in a function, this would often
    be much easier.

    > Just do it :)


    Yes, please do!

    > I would suggest a slight modification:
    >
    > t = timeit.Timer.for_function(foo, x, y, a=..., b=...)
    >
    > And while you are at it you could also move the code that dynamically
    > adjusts the number of repetitions from the main() function into a Timer
    > method and make its invocation the default for the timeit() method.


    I agree.

    Moreover, I think the calibration could be improved a lot. What I use
    is below. It would be great if something like this could be merged
    into timeit.py...

    Notice how quickly it locks onto an appropriate number of iterations.
    (Only 3% of the time is spent on calibration.)

    >>> import timer
    >>> timer.timing("3*2/6", goal=0.5, repeat=3, verbose=True)

    1 loops -> 1e-05 secs
    49933 loops -> 0.0396 secs
    630325 loops -> 0.499 secs
    630325 loops -> 0.499 secs 0.5 secs 0.501 secs
    630325 loops, best of 3 trials: 0.792 usec per loop

    Dan


    """Uses timeit to benchmark code, quickly choosing number of iterations.

    Differences:

    - This can be used interactively from python, not just from the
    command line.
    - Doesn't use separate creation and timing steps.
    - The algorithm for choosing the number of iterations is faster
    and more accurate. It also allows for just 1 iteration.
    - The result from the final pass through the convergence loop
    is used as one of the samples, which saves time
    (especially if repeat=1).

    Sample usage:

    import timer
    timer.timing("sin(10)",setup="from math import sin",repeat=3)
    import time
    timer.timing("3*2/6",repeat=3,goal=0.5,timer=time.time)

    To do:

    - add support for passing in a function instead of a string.
    """

    import timeit
    import time

    def timing(stmt="pass",
    setup="pass",
    repeat=1,
    goal=None,
    number=None,
    timer=timeit.default_timer,
    verbose=False,
    precision=3):
    '''Eval setup, and then time stmt.
    goal is desired time in seconds, a float.
    number is desired number of iterations.
    At most one of goal and number can be specified.
    If neither is specified, default to a goal of 1 second.
    When number is not specified, it is determined in an
    efficient way.
    repeat is the total number of times to run the test.
    timer is the timer to use.
    if verbose, show information about calibration.
    precision is the number of significant digits to display.
    '''

    t = timeit.Timer(stmt,setup,timer=timer)
    r = []

    if number != None and goal != None:
    raise ValueError,'Only one of number and goal may be specified.'

    if number == None:
    if goal == None:
    goal = 1.0
    # determine number so that total time >= 90% of goal.
    number = 1
    x = 0.0
    while x < 0.9*goal:
    x = t.timeit(number)

    if verbose:
    print "%9d loops -> %.*g secs" % (number, precision, x)
    if x == 0:
    # 1e2 is chosen because this is a common resolution
    # for time.clock. Make sure that number gets increased
    # by at least one, to avoid an infinite loop.
    number = max(number+1,int(number*1e2*goal))
    elif x < 0.9*goal:
    # If x is very small, goal/x is large (possibly inf);
    # to be cautious we limit the ratio to 1e6.
    # The +1 is to ensure that number increases by at least 1,
    # since goal/x > 1.
    number = int(number*min(1e6,goal/x)+1)
    r=[x]
    repeat -= 1

    r.extend(t.repeat(repeat, number))

    best = min(r)
    if verbose:
    print "%9d loops ->" % number,
    print " ".join(["%.*g secs" % (precision, x) for x in r])
    print "%9d loops, best of %d trials:" % (number, len(r)),
    usec = best * 1e6 / number
    if usec < 1000:
    print "%.*g usec per loop" % (precision, usec)
    elif usec < 1e6:
    print "%.*g milliseconds per loop" % (precision, usec/1000)
    else:
    print "%.*g seconds per loop" % (precision, usec/1e6)
     
    Dan Christensen, Feb 20, 2006
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Dan Christensen
    Replies:
    4
    Views:
    605
    Peter Otten
    Jul 14, 2004
  2. Wayne  Wengert

    Missing Something Obvious Here!

    Wayne Wengert, Aug 30, 2003, in forum: ASP General
    Replies:
    0
    Views:
    108
    Wayne Wengert
    Aug 30, 2003
  3. Ray Cote
    Replies:
    0
    Views:
    123
    Ray Cote
    Jan 3, 2013
  4. Barry Scott
    Replies:
    0
    Views:
    114
    Barry Scott
    Jan 3, 2013
  5. Ray Cote
    Replies:
    0
    Views:
    109
    Ray Cote
    Jan 4, 2013
Loading...

Share This Page