Spec-Zone.ru › Python 3.10

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

сколько раз выполнить ‘указание’

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

Spec-Zone.ru

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