Я хотел бы измерить время работы для двух кодов, я попытался найти документацию по python для timeit, но я действительно не понял. Может ли кто-нибудь объяснить в словаре более начального уровня?
Попытка использовать timeit.timeit
Ответы (3)
Примечание. Скопировано в Как использовать модуль timeit.
Открою вам секрет: лучше всего использовать 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
, как показано выше.
Предупреждения
Есть несколько предостережений относительно 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
Обратите внимание, что здесь разумно вычитать накладные расходы только потому, что накладные расходы составляют очень небольшую часть времени.
Я считаю, что магические функции IPython %timeit и %%timeit проще в использовании, чем timeit.timeit (особенно при использовании блокнота ipython). Пара примеров здесь.
>>> "-".join(str(n) for n in range(100))
'0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40-41-42-43-44-45-46-47-48-49-50-51-52-53-54-55-56-57-58-59-60-61-62-63-64-65-66-67-68-69-70-71-72-73-74-75-76-77-78-79-80-81-82-83-84-85-86-87-88-89-90-91-92-93-94-95-96-97-98-99'
>>>
Предположим, вы хотите выполнить эту команду.
Импортируйте timeit
. Сделайте команду строкой, добавьте, сколько раз вы хотите ее запустить.
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=100)
0.011214537887298093
Документация
Этот документ действительно непонятен? Я нахожу это ясным.