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.13/library/timeit.html