Spec-Zone.ru › Python 3.14

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

То же самое можно сделать с помощью интерфейса 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 с заданным оператором, кодом setup и функцией timer, а затем запускает его метод timeit(), выполняя number запусков. Необязательный аргумент globals задаёт пространство имён, в котором будет выполняться код.

Изменено в версии 3.5: Добавлен необязательный параметр globals.

timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)

Создаёт экземпляр Timer с заданным оператором, кодом setup и функцией 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] [-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 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/timeit.html

Spec-Zone.ru

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