Я розумію концепцію того, що timeit
робить, але не впевнений, як це реалізувати у своєму коді.
Як я можу порівняти дві функції, скажімо, insertion_sort
і tim_sort
з timeit
?
Я розумію концепцію того, що timeit
робить, але не впевнений, як це реалізувати у своєму коді.
Як я можу порівняти дві функції, скажімо, insertion_sort
і tim_sort
з timeit
?
Відповіді:
Спосіб роботи 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. Сподіваюсь, це допомагає :-)
.repeat(7,1000)
уже робить це (використовуючи те саме насіння)! Тож ваше рішення - ідеальний ІМО.
.repeat(7, 1000)
проти .repeat(2, 3500)
vs .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__
оператора настройки:
>>> 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
Запустивши це, ви можете побачити, що індекс набагато швидше, ніж отримати. Ви можете запустити його кілька разів, щоб побачити.
Код в основному намагається отримати значення 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
Приклад використання інтерпретатора REPL Python з функцією, яка приймає параметри.
>>> 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)
і прийміть різницю :-)