Как использовать модуль timeit - PullRequest
306 голосов
/ 22 ноября 2011

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

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

Ответы [ 14 ]

245 голосов
/ 22 ноября 2011

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

  1. Используйте оболочку 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
    
  2. В стандартном интерпретаторе 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]
    
245 голосов
/ 22 ноября 2011

Способ работы 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

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

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

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

120 голосов
/ 08 июня 2014

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

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

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

%~> 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 мкс.Вы только знаете, что это меньше , чем это.Все ошибки положительны.

    Так что попробуйте и найдите pure overhead:

    >>> 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
    

    Обратите внимание, что здесь разумно вычитать накладные расходы только потому, что накладные расходы составляют небольшую долю времени.Например, стоит отметить, что и Insertion Sort, и Tim Sort имеют совершенно необычные временные характеристики для уже отсортированных списков.Это означает, что вам потребуется random.shuffle между сортами, если вы хотите избежать нарушения времени.

83 голосов
/ 08 апреля 2015

Если вы хотите быстро сравнить два блока кода / функции, вы можете сделать:

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)
41 голосов
/ 22 ноября 2011

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

Задано test.py :

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

Запустить время так:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'
13 голосов
/ 17 января 2018

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

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


timeit.timeit(stmt=foo, number=1234567)
11 голосов
/ 02 апреля 2017
# Генерация целых чисел

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 голосов
/ 21 июля 2017

Это прекрасно работает:

  python -m timeit -c "$(cat file_name.py)"
3 голосов
/ 26 сентября 2016

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

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

Число - для запуска кода 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,2000007452246427

по получению: 0,54841166886888

2 голосов
/ 04 октября 2017

просто передайте весь ваш код как аргумент 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))
...