timeit — измерение времени выполнения небольших фрагментов кода
Исходный код: Lib/timeit.py
Этот модуль предоставляет простой способ измерить время выполнения небольших фрагментов кода Python. Он имеет как интерфейс командной строки, так и вызываемый интерфейс. Модуль позволяет избежать ряда распространённых ошибок при измерении времени выполнения. См. также введение Тима Питерса к главе «Алгоритмы» второго издания Python Cookbook, опубликованного издательством O’Reilly.
Основные примеры
В следующем примере показано, как с помощью интерфейса командной строки сравнить три разных выражения:
$ 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
Модуль определяет три вспомогательные функции и один открытый класс:
-
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 секунды.Если задан параметр 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] [-p] [-v] [-h] [statement ...]
Доступны следующие параметры:
-
-n N, --number=N -
сколько раз выполнить «инструкцию»
-
-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
В выводе есть три поля. Количество циклов показывает, сколько раз выполнялось тело оператора за одно повторение цикла измерения. Количество повторений («лучший из 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 >>> # 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/timeit.html