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
Это можно сделать из вызываемого интерфейса с помощью:
>>> 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.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с заданным оператором, кодом подготовки и функцией таймера и выполняет его методtimeit()с заданным количеством выполнений. Необязательный аргумент globals указывает пространство имен, в котором нужно выполнить код.Изменено в версии 3.5: Добавлен необязательный параметр globals.
-
timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None) -
Создает экземпляр
Timerс заданным оператором, кодом подготовки и функцией таймера и выполняет его методrepeat()с заданным числом повторений и числом выполнений. Необязательный аргумент 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/timeit.html