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