Spec-Zone.ru › Python 3.13

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–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/timeit.html

Spec-Zone.ru

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