Как использовать модуль timeit

Я понимаю концепцию того, что делает timeit, но я не уверен, как ее реализовать в моем коде.

Как сравнить две функции, например insertion_sort и tim_sort, с timeit?

Ответ 1

Способ timeit заключается в том, чтобы запускать код установки один раз, а затем повторять вызовы в ряд операторов. Итак, если вы хотите протестировать сортировку, требуется некоторая осторожность, так что один проход на месте сортировки не влияет на следующий проход с уже отсортированными данными (что, конечно же, сделает Timsort действительно сияет, потому что он лучше всего работает, когда данные уже частично упорядочены).

Вот пример настройки теста для сортировки:

>>> import timeit

>>> setup = '''
import random

random.seed('slartibartfast')
s = [random.random() for i in range(1000)]
timsort = list.sort
'''

>>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000))
0.334147930145

Обратите внимание, что ряд операторов делает новую копию несортированных данных на каждом проходе.

Кроме того, обратите внимание на методику синхронизации запуска набора измерений семь раз и сохраняя только лучшее время - это действительно может помочь уменьшить искажения измерений из-за других процессов, запущенных в вашей системе.

Это мои советы по правильному использованию timeit. Надеюсь, это поможет: -)

Ответ 2

Если вы хотите использовать timeit в интерактивном сеансе Python, есть два удобных варианта:

  • Используйте IPython оболочку. Он имеет удобную специальную функцию %timeit:

    In [1]: def f(x):
       ...:     return x*x
       ...: 
    
    In [2]: %timeit for x in range(100): f(x)
    100000 loops, best of 3: 20.3 us per loop
    
  • В стандартном интерпретаторе Python вы можете получить доступ к функциям и другим именам, которые вы определили ранее во время интерактивного сеанса, импортировав их из __main__ в инструкции установки:

    >>> def f(x):
    ...     return x * x 
    ... 
    >>> import timeit
    >>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f",
                      number=100000)
    [2.0640320777893066, 2.0876040458679199, 2.0520210266113281]
    

Ответ 3

Я дам вам секрет: лучший способ использовать timeit - в командной строке.

В командной строке timeit делает правильный статистический анализ: он сообщает вам, сколько времени занимает самый короткий пробег. Это хорошо, потому что все ошибки во времени положительны. Таким образом, самое короткое время имеет наименьшую ошибку. Там нет способа получить отрицательную ошибку, потому что компьютер не может вычислить быстрее, чем он может вычислить!

Итак, интерфейс командной строки:

%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop

Это довольно просто, а?

Вы можете настроить материал:

%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop

что тоже полезно!

Если вы хотите несколько строк, вы можете использовать автоматическое продолжение оболочки или использовать отдельные аргументы:

%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop

Это дает

x = range(1000)
y = range(100)

и раз

sum(x)
min(y)

Если вы хотите иметь более длинные скрипты, у вас может возникнуть соблазн перейти на timeit внутри скрипта Python. Я предлагаю избегать этого, потому что анализ и синхронизация просто лучше в командной строке. Вместо этого я стараюсь создавать сценарии оболочки:

 SETUP="

 ... # lots of stuff

 "

 echo Minmod arr1
 python -m timeit -s "$SETUP" "Minmod(arr1)"

 echo pure_minmod arr1
 python -m timeit -s "$SETUP" "pure_minmod(arr1)"

 echo better_minmod arr1
 python -m timeit -s "$SETUP" "better_minmod(arr1)"

 ... etc

Это может занять несколько больше времени из-за множественных инициализаций, но обычно это не очень важно.


Но что, если вы хотите использовать timeit внутри своего модуля?

Ну, простой способ сделать:

def function(...):
    ...

timeit.Timer(function).timeit(number=NUMBER)

и это дает вам кумулятивное (не минимальное!) время для запуска этого количества раз.

Чтобы получить хороший анализ, используйте .repeat и возьмите минимум:

min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))

Обычно вы должны комбинировать это с functools.partial вместо lambda:... для снижения накладных расходов. Таким образом, у вас может быть что-то вроде:

from functools import partial

def to_time(items):
    ...

test_items = [1, 2, 3] * 100
times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000)

# Divide by the number of repeats
time_taken = min(times) / 1000

Вы также можете сделать:

timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)

который даст вам что-то ближе к интерфейсу из командной строки, но в гораздо менее прохладной манере. "from __main__ import..." позволяет использовать код из основного модуля внутри искусственной среды, созданной timeit.

Стоит отметить, что это удобная обертка для Timer(...).timeit(...) и поэтому это не очень хорошо подходит для синхронизации. Я лично предпочитаю использовать Timer(...).repeat(...) как я показал выше.


Предупреждения

Есть несколько предостережений с timeit которые держатся везде.

  • Накладные расходы не учитываются. Скажем, вы хотите, чтобы время x += 1, чтобы узнать, сколько времени требуется:

    >>> python -m timeit -s "x = 0" "x += 1"
    10000000 loops, best of 3: 0.0476 usec per loop
    

    Ну, это не 0.0476 мкс. Вы только знаете, что это меньше этого. Вся ошибка положительная.

    Поэтому попробуйте найти чистые накладные расходы:

    >>> python -m timeit -s "x = 0" ""      
    100000000 loops, best of 3: 0.014 usec per loop
    

    Это хорошая 30% накладных расходов только с момента! Это может значительно исказить относительные тайминги. Но вы действительно заботились о добавлении таймингов; временные интервалы поиска для x также должны быть включены в накладные расходы:

    >>> python -m timeit -s "x = 0" "x"
    100000000 loops, best of 3: 0.0166 usec per loop
    

    Разница не намного больше, но она есть.

  • Методы мутирования опасны.

    >>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
    10000000 loops, best of 3: 0.0436 usec per loop
    

    Но это совершенно неправильно! x - пустой список после первой итерации. Вам нужно будет повторно инициализировать:

    >>> python -m timeit "x = [0]*100000" "while x: x.pop()"
    100 loops, best of 3: 9.79 msec per loop
    

    Но тогда у вас много накладных расходов. Учтите это отдельно.

    >>> python -m timeit "x = [0]*100000"                   
    1000 loops, best of 3: 261 usec per loop
    

    Обратите внимание, что вычитание накладных расходов разумно здесь только потому, что служебные данные представляют собой небольшую часть времени.

    Для вашего примера стоит отметить, что как сортировка вставки, так и сортировка по типу имеют совершенно необычные настройки времени для уже отсортированных списков. Это означает, что вам понадобится random.shuffle между сортировками, если вы хотите избежать разрушения ваших таймингов.

Ответ 4

Если вы хотите быстро сравнить два блока кода/функций, выполните следующие действия:

import timeit

start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func2()
print(timeit.default_timer() - start_time)

Ответ 5

Я считаю, что самый простой способ использовать timeit - из командной строки:

Учитывая test.py:

def InsertionSort(): ...
def TimSort(): ...

запустить timeit следующим образом:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'

Ответ 6

# Генерация целых чисел

def gen_prime(x):
    multiples = []
    results = []
    for i in range(2, x+1):
        if i not in multiples:
            results.append(i)
            for j in range(i*i, x+1, i):
                multiples.append(j)

    return results


import timeit

# Засекаем время

start_time = timeit.default_timer()
gen_prime(3000)
print(timeit.default_timer() - start_time)

# start_time = timeit.default_timer()
# gen_prime(1001)
# print(timeit.default_timer() - start_time)

Ответ 7

для меня это самый быстрый способ:

import timeit
def foo():
    print("here is my code to time...")


timeit.timeit(stmt=foo, number=1234567)

Ответ 8

Это отлично работает:

  python -m timeit -c "$(cat file_name.py)"

Ответ 9

позволяет установить один и тот же словарь в каждом из следующих и проверить время выполнения.

Аргумент настройки в основном устанавливает словарь

Число - это запуск кода 1000000 раз. Не настройка, а stmt

Когда вы запустите это, вы увидите, что индекс быстрее, чем get. Вы можете запустить его несколько раз, чтобы увидеть.

В основном код пытается получить значение c в словаре.

import timeit

print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))
print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))

Вот мои результаты, ваши будут отличаться.

по индексу: 0.20900007452246427

: get: 0.54841166886888

Ответ 10

просто передайте весь код в качестве аргумента timeit:

import timeit

print(timeit.timeit("""

limit = 10000
prime_list = [i for i in range(2, limit+1)]

for prime in prime_list:
    for elem in range(prime*2, max(prime_list)+1, prime):
        if elem in prime_list:
            prime_list.remove(elem)"""

, number=10))

Ответ 11

Встроенный модуль timeit лучше всего работает из командной строки IPython.

В функции времени изнутри модуля:

from timeit import default_timer as timer
import sys

def timefunc(func, *args, **kwargs):
    """Time a function. 

    args:
        iterations=3

    Usage example:
        timeit(myfunc, 1, b=2)
    """
    try:
        iterations = kwargs.pop('iterations')
    except KeyError:
        iterations = 3
    elapsed = sys.maxsize
    for _ in range(iterations):
        start = timer()
        result = func(*args, **kwargs)
        elapsed = min(timer() - start, elapsed)
    print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed)))
    return result

Ответ 12

Пример использования интерпретатора Python REPL с функцией, которая принимает параметры.

>>> import timeit                                                                                         

>>> def naive_func(x):                                                                                    
...     a = 0                                                                                             
...     for i in range(a):                                                                                
...         a += i                                                                                        
...     return a                                                                                          

>>> def wrapper(func, *args, **kwargs):                                                                   
...     def wrapper():                                                                                    
...         return func(*args, **kwargs)                                                                  
...     return wrapper                                                                                    

>>> wrapped = wrapper(naive_func, 1_000)                                                                  

>>> timeit.timeit(wrapped, number=1_000_000)                                                              
0.4458435332577161