Содержание страницы
timeit – Измерение времени выполнения небольших фрагментов кода¶timeit – Measure execution time of small code snippets
Исходный код: Lib/timeit.py
Этот модуль предоставляет простой способ измерения времени выполнения небольших фрагментов кода Python. Он имеет как интерфейс командной строки, так и вызываемый объект. Модуль избегает множества распространённых ловушек при измерении времени выполнения. См. также введение Тима Питерса к главе «Алгоритмы» в Python Cookbook, опубликованной O’Reilly.
Простые примеры¶Basic Examples
Следующий пример показывает, как можно использовать интерфейс командной строки для сравнения трёх различных выражений:
$ 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¶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().Изменено в версии 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 раз. Сначала выполняется оператор setup один раз, затем возвращается время, затраченное на выполнение основного оператора указанное количество раз, измеряемое в секундах в виде числа с плавающей запятой. Аргумент number – количество повторений цикла, по умолчанию миллион. Основной оператор, оператор setup и используемая функция таймера передаются конструктору.
Примечание
По умолчанию
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] [-h] [statement ...]
Где поддерживаются следующие параметры:
-
-nN,--number=N¶ сколько раз выполнять 'statement'
-
-rN,--repeat=N¶ сколько раз повторить таймер (по умолчанию 5)
-
-sS,--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
>>> 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()))