timeit — Измерение времени выполнения небольших фрагментов кода
Исходный код: Lib/timeit.py
Этот модуль предоставляет простой способ измерения времени выполнения небольших фрагментов кода Python. Он имеет как интерфейс командной строки, так и вызываемый интерфейс. Он избегает ряда общих ловушек при измерении времени выполнения. См. также введение Тима Петерса в главу «Алгоритмы» в книге «Python Cookbook», опубликованной издательством O’Reilly.
Основные примеры
Следующий пример демонстрирует, как можно использовать интерфейс командной строки для сравнения трех разных выражений:
$ python3 -m timeit '"-".join(str(n) for n in range(100))' 10000 loops, best of 5: 30.2 usec per loop $ python3 -m timeit '"-".join([str(n) for n in range(100)])' 10000 loops, best of 5: 27.5 usec per loop $ python3 -m timeit '"-".join(map(str, range(100)))' 10000 loops, best of 5: 23.2 usec per loop
Это можно сделать с помощью интерфейса Python:
>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.3018611848820001
>>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
0.2727368790656328
>>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)
0.23702679807320237
Вызываемый объект также можно передать из интерфейса Python:
>>> timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000) 0.19665591977536678
Однако обратите внимание, что timeit() автоматически определит количество повторений только при использовании интерфейса командной строки. В разделе «Примеры» вы найдете более продвинутые примеры.
Интерфейс Python
Модуль определяет три удобные функции и один публичный класс:
-
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None) -
Создает экземпляр
Timerс заданным оператором, кодом настройки и функцией таймера и выполняет методtimeit()с заданным количеством запусков. Дополнительный аргумент globals определяет пространство имен, в котором будет выполняться код.Изменено в версии 3.5: Добавлен необязательный параметр globals.
-
timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None) -
Создает экземпляр
Timerс заданным оператором, кодом настройки и функцией таймера и выполняет методrepeat()с заданным количеством повторений и количеством запусков. Дополнительный аргумент globals определяет пространство имен, в котором будет выполняться код.Изменено в версии 3.5: Добавлен необязательный параметр globals.
Изменено в версии 3.7: Значение по умолчанию для repeat изменено с 3 на 5.
-
timeit.default_timer() -
Функция таймера по умолчанию, которая всегда является
time.perf_counter().Изменено в версии 3.3:
time.perf_counter()теперь является таймером по умолчанию.
-
class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None) -
Класс для измерения скорости выполнения небольших фрагментов кода.
Конструктор принимает оператор для измерения времени, дополнительный оператор для настройки и функцию таймера. Оба оператора по умолчанию равны
'pass'; функция таймера зависит от платформы (см. строку документации модуля). stmt и setup также могут содержать несколько операторов, разделенных;или символами новой строки, если они не содержат многострочные строковые литералы. Оператор по умолчанию будет выполняться в пространстве имен timeit; это поведение можно контролировать, передавая пространство имен в globals.Для измерения времени выполнения первого оператора используйте метод
timeit(). Методыrepeat()иautorange()являются удобными методами для вызоваtimeit()несколько раз.Время выполнения setup не включается в общее время выполнения.
Параметры stmt и setup также могут принимать объекты, которые вызываемы без аргументов. Это встроит вызовы в функцию таймера, которая затем будет выполнена
timeit(). Обратите внимание, что накладные расходы на время в этом случае немного больше из-за дополнительных вызовов функций.Изменено в версии 3.5: Добавлен необязательный параметр globals.
-
timeit(number=1000000) -
Измеряет время выполнения number запусков основного оператора. Это выполняет оператор настройки один раз и затем возвращает время, затраченное на выполнение основного оператора определенное количество раз, измеренное в секундах как число с плавающей точкой. Аргумент — количество проходов цикла, по умолчанию равное одному миллиону. Основной оператор, оператор настройки и функция таймера передаются в конструктор.
Примечание
По умолчанию
timeit()временно отключает сборку мусора во время измерения. Преимущество этого подхода заключается в том, что независимые замеры становятся более сопоставимыми. Недостатком является то, что сборка мусора может быть важной частью производительности измеряемой функции. Если это так, сборку мусора можно снова включить как первый оператор в строке setup. Например:timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
-
autorange(callback=None) -
Автоматически определяет количество вызовов
timeit().Это удобная функция, которая многократно вызывает
timeit(), чтобы общее время было не менее 0,2 секунды, возвращая окончательное количество циклов и затраченное на них время. Он вызываетtimeit()с увеличивающимися числами из последовательности 1, 2, 5, 10, 20, 50, … до тех пор, пока затраченное время не станет не менее 0,2 секунды.Если указана функция callback и она не
None, она будет вызываться после каждой попытки с двумя аргументами:callback(number, time_taken).Новое в версии 3.6.
-
repeat(repeat=5, number=1000000) -
Выполнить несколько вызовов
timeit().Это удобная функция, которая многократно вызывает
timeit(), возвращая список результатов. Первый аргумент задает количество вызововtimeit(). Второй аргумент задает аргумент number дляtimeit().Примечание
Вполне соблазнительно вычислить среднее значение и стандартное отклонение из вектора результатов и сообщить эти данные. Однако это не очень полезно. В типичном случае наименьшее значение задает нижнюю границу скорости выполнения заданного фрагмента кода вашей машиной; более высокие значения в векторе результатов обычно не вызваны вариабельностью скорости Python, а другими процессами, которые вмешиваются в точность измерения. Поэтому
min()результата, вероятно, является единственным числом, которое вас должно интересовать. После этого следует посмотреть на весь вектор и применить здравый смысл, а не статистику.Изменено в версии 3.7: Значение по умолчанию для repeat изменено с 3 на 5.
-
print_exc(file=None) -
Вспомогательная функция для вывода отладки трассировки стека для времени выполнения кода.
Типичное использование:
t = Timer(...) # outside the try/except try: t.timeit(...) # or t.repeat(...) except Exception: t.print_exc()Преимущество перед стандартной трассировкой стека состоит в том, что будут показаны строки исходного кода в скомпилированной модели. Необязательный аргумент file указывает, куда будет отправлена трассировка стека; по умолчанию он равен
sys.stderr.
-
Интерфейс командной строки
При вызове программы из командной строки используется следующий формат:
python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...]
Где следующие опции понятны:
-
-n N, --number=N -
сколько раз выполнить оператор 'statement'
-
-r N, --repeat=N -
сколько раз повторить замер времени (по умолчанию 5)
-
-s S, --setup=S -
оператор, который будет выполняться один раз вначале (по умолчанию
pass)
-
-p, --process -
измерить время выполнения процесса, а не время, затраченное на выполнение, используя
time.process_time()вместоtime.perf_counter(), что является значением по умолчаниюНовое в версии 3.3.
-
-u, --unit=U -
указать единицу измерения времени для вывода результатов; можно выбрать nsec, usec, msec или sec
Новое в версии 3.5.
-
-v, --verbose -
вывести результаты измерения времени; повторить для большей точности
-
-h, --help -
вывести короткое сообщение об использовании и выйти
Многострочный оператор может быть задан указанием каждой строки как отдельного аргумента оператора; вложенные строки возможны путем помещения аргумента в кавычки и использования начальных пробелов. Несколько -s опций обрабатываются аналогично.
Если -n не указан, подходящее количество циклов вычисляется путем попытки увеличения чисел из последовательности 1, 2, 5, 10, 20, 50, … до тех пор, пока общее время не будет составлять по крайней мере 0,2 секунды.
default_timer() измерения могут быть повлияны другими программами, работающими на том же компьютере, поэтому лучше всего, когда необходима точность измерения времени, повторить измерение несколько раз и использовать наилучшее время. Опция -r подходит для этого; по умолчанию 5 повторов, вероятно, достаточно в большинстве случаев. Вы можете использовать time.process_time() для измерения времени процессора.
Примечание
Существует определенная базовая накладная стоимость, связанная с выполнением оператора pass. Здесь код не пытается скрыть его, но вы должны об этом знать. Базовую накладную стоимость можно измерить, вызвав программу без аргументов, и она может отличаться в разных версиях Python.
Примеры
Можно указать оператор настройки, который выполняется только один раз в начале:
$ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text' 5000000 loops, best of 5: 0.0877 usec per loop $ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)' 1000000 loops, best of 5: 0.342 usec per loop
>>> import timeit
>>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')
0.41440500499993504
>>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')
1.7246671520006203
То же самое можно сделать, используя класс Timer и его методы:
>>> import timeit
>>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')
>>> t.timeit()
0.3955516149999312
>>> t.repeat()
[0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]
Следующие примеры показывают, как измерить время выполнения выражений, содержащих несколько строк. Здесь мы сравниваем стоимость использования hasattr() по сравнению с try/except для проверки отсутствия и присутствия атрибутов объекта:
$ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass' 20000 loops, best of 5: 15.7 usec per loop $ python -m timeit 'if hasattr(str, "__bool__"): pass' 50000 loops, best of 5: 4.26 usec per loop $ python -m timeit 'try:' ' int.__bool__' 'except AttributeError:' ' pass' 200000 loops, best of 5: 1.43 usec per loop $ python -m timeit 'if hasattr(int, "__bool__"): pass' 100000 loops, best of 5: 2.23 usec per loop
>>> import timeit >>> # attribute is missing >>> s = """\ ... try: ... str.__bool__ ... except AttributeError: ... pass ... """ >>> timeit.timeit(stmt=s, number=100000) 0.9138244460009446 >>> s = "if hasattr(str, '__bool__'): pass" >>> timeit.timeit(stmt=s, number=100000) 0.5829014980008651 >>> >>> # attribute is present >>> s = """\ ... try: ... int.__bool__ ... except AttributeError: ... pass ... """ >>> timeit.timeit(stmt=s, number=100000) 0.04215312199994514 >>> s = "if hasattr(int, '__bool__'): pass" >>> timeit.timeit(stmt=s, number=100000) 0.08588060699912603
Чтобы предоставить модулю timeit доступ к определенным вами функциям, можно передать параметр setup, который содержит оператор импорта:
def test():
"""Stupid test function"""
L = [i for i in range(100)]
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test"))
Другой вариант — передать globals() в параметр globals, что приведет к выполнению кода в текущем глобальном пространстве имен. Это может быть удобнее, чем индивидуальное указание импорта:
def f(x):
return x**2
def g(x):
return x**4
def h(x):
return x**8
import timeit
print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/timeit.html