Как использовать модуль 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