Tony Babarino
Tony Babarino

Reputation: 3405

Python timeit: How to initialize arguments for timed function before every repetition?

Let's say I'm trying to measure the execution time of a sorting function, here is my code:

import timeit

def time_algo(sort_fun, input_seq, num=100):
    '''
    Time how long it takes to sort sequence 'input_seq' using
    function 'sort_fun'. Take min of 'num' times.
    '''
    foo = list(input_seq)
    wrapped = wrapper(sort_fun, foo)
    return min(timeit.repeat(wrapped, repeat=num, number=1))

def wrapper(func, *args):
    def wrapped():
        return func(*args)
    return wrapped

print time_algo(list.sort, [10,9,8,7,6,5,4,3,2,1], num = 100)

The problem is that after the first execution of sort_fun, the input list is already sorted, and it's running on a sorted list for the rest of num - 1 times.

How can I initialize the arguments for the input function (in this case execute foo = list(input_seq)) before every repetition of function timing? Or what would be the proper way to do this using the timeit module (I need accurate results so i don't want to use other timing methods like time.clock() etc.)?

Upvotes: 2

Views: 808

Answers (1)

Pierce
Pierce

Reputation: 526

I think this modified time_algo() does what you want:

def time_algo(sort_fun, input_seq, num=100):                                                        
    '''                                                                                             
    Time how long it takes to sort sequence 'input_seq' using                                       
    function 'sort_fun'. Take min of 'num' times.                                                   
    '''                                                                                             
    foo = list(input_seq)                                                                           
    wrapped = wrapper(sort_fun, foo)                                                                
    def reset_foo():                                                                                
        foo[:] = list(input_seq)                                                                    
    return min(timeit.timeit(wrapped, setup=reset_foo, number=1) for _ in range(num))

This re-copies input_seq to list foo before each timeit(). There less-fancy / more-manual ways of doing it that could be more general: you could write a plain loop which copies the input, makes a new wrapper, and calls timeit.

... but as commented above, you really want multiple iterations, per timeit, and for that you really want to be timing a function which does not modify its inputs.

Upvotes: 3

Related Questions