Spec-Zone.ru › Python 3.11

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() с number выполнения. Необязательный аргумент 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(), возвращает секунды с плавающей точкой. Альтернатива, 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] [-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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/timeit.html

Spec-Zone.ru

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