timeit — Измерение времени выполнения небольших фрагментов кода
Исходный код: Lib/timeit.py
Этот модуль предоставляет простой способ измерения времени выполнения небольших фрагментов кода Python. Он имеет как интерфейс командной строки, так и вызываемый. Он избегает ряда распространенных ловушек при измерении времени выполнения. Также обратите внимание на введение Тима Питерса в главу «Алгоритмы» в книге «Python Cookbook», изданной издательством O’Reilly.
27.5.1. Основные примеры
Следующий пример демонстрирует, как интерфейс командной строки может быть использован для сравнения трех различных выражений:
$ python3 -m timeit '"-".join(str(n) for n in range(100))' 10000 loops, best of 3: 30.2 usec per loop $ python3 -m timeit '"-".join([str(n) for n in range(100)])' 10000 loops, best of 3: 27.5 usec per loop $ python3 -m timeit '"-".join(map(str, range(100)))' 10000 loops, best of 3: 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
Обратите внимание, что timeit автоматически определит количество повторений только при использовании интерфейса командной строки. В разделе Примеры вы найдёте более сложные примеры.
27.5.2. Интерфейс Python
Модуль определяет три удобные функции и один публичный класс:
-
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None) -
Создаёт экземпляр
Timerс заданным оператором, кодом настройки и функцией таймера и запускает его методtimeit()с заданным количеством исполнений number. Необязательный аргумент globals задаёт пространство имён, в котором будет выполняться код.Изменено в версии 3.5: Добавлен необязательный параметр globals.
-
timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=3, number=1000000, globals=None) -
Создаёт экземпляр
Timerс заданным оператором, кодом настройки и функцией таймера и запускает его методrepeat()с заданным количеством повторений repeat и количеством исполнений number. Необязательный аргумент globals задаёт пространство имён, в котором будет выполняться код.Изменено в версии 3.5: Добавлен необязательный параметр globals.
-
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()временно отключает сбор мусора во время измерения. Преимущество этого подхода заключается в том, что независимые измерения становятся более сопоставимыми. Недостаток в том, что GC может быть важной частью производительности измеряемой функции. Если это так, GC можно включить как первый оператор в строке setup. Например:timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
-
autorange(callback=None) -
Автоматически определяет, сколько раз нужно вызывать
timeit().Это удобная функция, которая многократно вызывает
timeit()таким образом, что общее время >= 0,2 секунды, возвращая конечное (количество циклов, время, затраченное на это количество циклов). Она вызываетtimeit()со значениями number, равными последовательным степеням десяти (10, 100, 1000, …) до максимального значения в один миллиард, пока время выполнения не станет не менее 0,2 секунды или не будет достигнута максимальная граница.Если задан callback и он не
None, он будет вызываться после каждой пробы с двумя аргументами:callback(number, time_taken).Добавлен в версии 3.6.
-
repeat(repeat=3, number=1000000) -
Вызывает
timeit()несколько раз.Это удобная функция, которая вызывает метод
timeit()многократно, возвращая список результатов. Первый аргумент указывает, сколько раз вызватьtimeit(). Второй аргумент указывает аргумент number дляtimeit().Примечание
Заманчиво вычислить среднее значение и стандартное отклонение из результативного вектора и сообщить эти значения. Однако это не очень полезно. В типичном случае наименьшее значение даёт нижнюю границу скорости выполнения вашего кода; большие значения в векторе результатов, как правило, не вызваны вариативностью скорости Python, а другими процессами, которые мешают точности измерения. Поэтому
min()результата, вероятно, единственное число, которое вас должно интересовать. После этого нужно посмотреть на весь вектор и применить здравый смысл, а не статистику.
-
print_exc(file=None) -
Помощник для вывода отладочной информации о выполнении кода.
Типичное использование:
t = Timer(...) # outside the try/except try: t.timeit(...) # or t.repeat(...) except Exception: t.print_exc()Преимущество перед стандартным отладочным выводом заключается в том, что строки исходного кода в скомпилированной шаблоне будут отображаться. Необязательный аргумент file определяет, куда будет выведен трассировочный вывод; по умолчанию он устанавливается в
sys.stderr.
-
27.5.3. Интерфейс командной строки
При вызове программы из командной строки используется следующий формат:
python -m timeit [-n N] [-r N] [-u U] [-s S] [-t] [-c] [-h] [statement ...]
Где поддерживаются следующие опции:
-
-n N, --number=N -
сколько раз выполнить ‘statement’
-
-r N, --repeat=N -
сколько раз повторить замер времени (по умолчанию 3)
-
-s S, --setup=S -
оператор, который выполняется один раз в начале (по умолчанию
pass)
-
-p, --process -
измерять время процесса, а не время wallclock, используя
time.process_time()вместоtime.perf_counter(), что является значением по умолчаниюДобавлен в версии 3.3.
-
-t, --time -
использовать
time.time()(устарело)
-
-u, --unit=U -
указать единицу времени для вывода таймера; можно выбрать usec, msec или sec
Новое в версии 3.5.
-
-c, --clock -
использовать
time.clock()(устарело)
-
-v, --verbose -
выводить результаты измерения времени в сыром виде; повторение для большей точности
-
-h, --help -
вывести краткое сообщение об использовании и завершить
Многострочное утверждение может быть задано, указав каждую строку как отдельный аргумент утверждения; вложенные строки возможны, поместив аргумент в кавычки и используя начальные пробелы. Несколько -s опций обрабатываются аналогично.
Если -n не задан, подходящее число циклов вычисляется, пытаясь последовательно возводить 10 в степень до тех пор, пока общее время не достигнет как минимум 0,2 секунды.
default_timer() измерения могут быть затронуты другими программами, работающими на том же компьютере, поэтому при необходимости точного измерения лучше всего повторить измерения несколько раз и использовать наилучшее время. Опция -r подходит для этого; по умолчанию 3 повторения, вероятно, достаточно в большинстве случаев. Вы можете использовать time.process_time() для измерения времени работы процессора.
Примечание
Есть определённые базовые накладные расходы, связанные с выполнением оператора pass. Код здесь не пытается их скрыть, но вы должны быть об этом осведомлены. Базовые накладные расходы можно измерить, вызвав программу без аргументов, и они могут отличаться между версиями Python.
27.5.4. Примеры
Можно указать оператор настройки, который выполняется только один раз в начале:
$ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text' 10000000 loops, best of 3: 0.0877 usec per loop $ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)' 1000000 loops, best of 3: 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.40193588800002544, 0.3960157959998014, 0.39594301399984033]
Следующие примеры показывают, как измерить время выполнения выражений, содержащих несколько строк. Здесь мы сравниваем стоимость использования hasattr() по сравнению с try/except для проверки наличия и отсутствия атрибутов объекта:
$ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass' 100000 loops, best of 3: 15.7 usec per loop $ python -m timeit 'if hasattr(str, "__bool__"): pass' 100000 loops, best of 3: 4.26 usec per loop $ python -m timeit 'try:' ' int.__bool__' 'except AttributeError:' ' pass' 1000000 loops, best of 3: 1.43 usec per loop $ python -m timeit 'if hasattr(int, "__bool__"): pass' 100000 loops, best of 3: 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–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.6/library/timeit.html