Содержание страницы
timeit – Измерение времени выполнения небольших фрагментов кода¶timeit – Measure execution time of small code snippets
Исходный код: Lib/timeit.py
Этот модуль предоставляет простой способ измерения времени выполнения небольших фрагментов кода Python. Он имеет как интерфейс командной строки, так и вызываемый. Он позволяет избежать множества типичных ловушек при измерении времени выполнения. Также см. введение Тима Питерса к главе «Алгоритмы» во втором издании Python Cookbook, опубликованном издательством O’Reilly.
Простые примеры¶Basic Examples
Следующий пример показывает, как можно использовать интерфейс командной строки для сравнения трёх различных выражений:
$ python -m timeit "'-'.join(str(n) for n in range(100))"
10000 loops, best of 5: 30.2 usec per loop
$ python -m timeit "'-'.join([str(n) for n in range(100)])"
10000 loops, best of 5: 27.5 usec per loop
$ python -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¶Python Interface
Модуль определяет три вспомогательные функции и один открытый класс:
- timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)¶
Создаёт экземпляр
Timerс заданными оператором, кодом setup и функцией timer и запускает его методtimeit()с number выполнениями. Необязательный аргумент globals задаёт пространство имён, в котором будет выполняться код.Изменено в версии 3.5: Был добавлен необязательный параметр globals.
- timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)¶
Создаёт экземпляр
Timerс заданными оператором, кодом setup и функцией timer и запускает его методrepeat()с заданным количеством repeat и number выполнениями. Необязательный аргумент globals задаёт пространство имён, в котором будет выполняться код.Изменено в версии 3.5: Был добавлен необязательный параметр globals.
Изменено в версии 3.7: Значение по умолчанию repeat изменено с 3 на 5.
- timeit.default_timer()¶
Таймер по умолчанию, который всегда равен time.perf_counter(), возвращает число секунд с плавающей точкой. Альтернатива time.perf_counter_ns возвращает целое число наносекунд.
Изменено в версии 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 секунды.Если задан колбэк и он не равен
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(...) # за пределами try/except try: t.timeit(...) # или t.repeat(...) except Exception: t.print_exc()
Преимущество перед стандартной трассировкой в том, что будут отображаться строки исходного кода в скомпилированном шаблоне. Необязательный аргумент file указывает, куда направляется трассировка; по умолчанию используется
sys.stderr.
Интерфейс командной строки¶Command-Line Interface
При вызове как программы из командной строки используется следующая форма:
python -m timeit [-n N] [-r N] [-u U] [-s S] [-p] [-v] [-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¶
вывести краткое сообщение об использовании и выйти
Многострочная инструкция может быть задана указанием каждой строки как отдельного
аргумента statement; строки с отступами возможны путём заключения аргумента в
кавычки и использования начальных пробелов. Несколько параметров -s обрабатываются
аналогично.
Если -n не задан, подходящее количество циклов вычисляется путём проб
возрастающих чисел из последовательности 1, 2, 5, 10, 20, 50, … пока общее
время не достигнет хотя бы 0.2 секунды.
На результаты измерений default_timer() могут влиять другие программы, выполняющиеся на
той же машине, поэтому при необходимости точных замеров лучше всего
повторить измерение несколько раз и взять наилучшее время. Параметр -r
подходит для этого; значения по умолчанию 5 повторений, вероятно, достаточно в
большинстве случаев. Можно использовать time.process_time() для измерения процессорного времени.
Примечание
Существует некоторая базовая накладная нагрузка, связанная с выполнением инструкции pass. Приведённый здесь код не пытается её скрыть, но о ней следует знать. Базовую накладную нагрузку можно измерить, вызвав программу без аргументов, и она может отличаться в разных версиях Python.
Примеры¶Examples
Можно указать инструкцию настройки, которая выполняется только один раз в начале:
$ 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
В выводе представлены три поля. Количество итераций цикла (loop count), показывающее, сколько раз тело оператора выполнялось за одно повторение цикла замера. Количество повторений (repetition count, «best of 5»), показывающее, сколько раз цикл замера повторялся, и, наконец, среднее время выполнения тела оператора в лучшем повторении цикла замера. То есть время, затраченное самым быстрым повторением, делённое на количество итераций цикла.
>>> 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
>>> # атрибут отсутствует
>>> 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
>>>
>>> # атрибут присутствует
>>> 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, содержащий инструкцию import:
def test():
"""Глупая тестовая функция"""
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()))