Ответы:
Способ работы 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
Обратите внимание, что серия операторов создает свежую копию несортированных данных на каждом проходе.
Кроме того, обратите внимание на методику синхронизации запуска набора измерений семь раз и сохранения только лучшего времени - это действительно может помочь уменьшить искажения измерения из-за других процессов, выполняющихся в вашей системе.
Это мои советы по правильному использованию времени. Надеюсь это поможет :-)
.repeat(7,1000)
уже делаете это (используя то же семя)! Таким образом, ваше решение идеально подходит для IMO.
.repeat(7, 1000)
против и .repeat(2, 3500)
против .repeat(35, 200
), должно зависеть от того, как ошибка из-за загрузки системы сравнивается с ошибкой из-за изменчивости ввода. В крайнем случае, если ваша система всегда находится под большой нагрузкой, и вы видите длинный тонкий хвост слева от распределения времени выполнения (когда вы ловите его в редком состоянии простоя), вы можете даже .repeat(7000,1)
оказаться более полезным, чем .repeat(7,1000)
если бы вы не может бюджетировать более 7000 пробежек.
Если вы хотите использовать 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__
оператора setup:
>>> 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]
from __main__ import f
техники. Я не думаю, что это так широко известно, как должно быть. Это полезно в случаях, подобных этому, когда происходит вызов функции или метода. В других случаях (синхронизация ряда шагов) это менее полезно, потому что вводит издержки вызова функции.
%timeit f(x)
sys._getframe(N).f_globals
) должны были быть по умолчанию с самого начала.
Я открою вам секрет: лучший способ использовать 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
перерывах между родами, если вы хотите избежать разрушения времени.
timeit
из программы, но функционирование так же, как в командной строке? ,
timeit
выполняет pass
оператор, когда аргументы не заданы, что, конечно, занимает некоторое время. Если будут заданы какие-либо аргументы, pass
они не будут выполнены, поэтому вычитание некоторых 0.014
usecs из каждого тайминга было бы некорректным.
для меня это самый быстрый способ:
import timeit
def foo():
print("here is my code to time...")
timeit.timeit(stmt=foo, number=1234567)
# Генерация целых чисел
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)
Это прекрасно работает:
python -m timeit -c "$(cat file_name.py)"
Давайте установим один и тот же словарь в каждом из следующих пунктов и протестируем время выполнения.
Аргумент настройки в основном настраивает словарь
Номер для запуска кода 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
по получению: 0.54841166886888
просто передайте весь код в качестве аргумента 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))
import timeit
def oct(x):
return x*x
timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()
gc.enable()
?
Встроенный модуль 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
Пример использования интерпретатора 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
Вы должны создать две функции, а затем запустить нечто похожее на это. Обратите внимание, вы хотите выбрать одинаковое количество выполнений / прогонов, чтобы сравнить яблоко с яблоком.
Это было проверено в Python 3.7.
Вот код для простоты его копирования
!/usr/local/bin/python3
import timeit
def fibonacci(n):
"""
Returns the n-th Fibonacci number.
"""
if(n == 0):
result = 0
elif(n == 1):
result = 1
else:
result = fibonacci(n-1) + fibonacci(n-2)
return result
if __name__ == '__main__':
import timeit
t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")
timsort(a)
и возьмите разницу :-)