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 -
сколько раз выполнить «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
В выводе есть три поля. Число циклов показывает, сколько раз тело оператора выполнялось за один повтор измерительного цикла. Число повторений («лучший из 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–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/timeit.html