Spec-Zone.ru › Python 3.8

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() с заданным количеством повторений и количеством запусков. Дополнительный аргумент 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–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/timeit.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API