Документация Python неофициальный перевод
Содержание страницы

26.6. timeit – Измерение времени выполнения небольших фрагментов кодаtimeit – Measure execution time of small code snippets

Новое в версии 2.3.

Исходный код: Lib/timeit.py


Этот модуль предоставляет простой способ измерения времени выполнения небольших фрагментов кода Python. Он имеет как интерфейс командной строки, так и вызываемый объект. Модуль избегает множества распространённых ловушек при измерении времени выполнения. См. также введение Тима Питерса к главе «Алгоритмы» в Python Cookbook, опубликованной O’Reilly.

26.6.1. Основные примерыBasic Examples

Следующий пример показывает, как можно использовать интерфейс командной строки для сравнения трёх различных выражений:

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop

Этого можно добиться из интерфейса Python с помощью:

>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.8187260627746582
>>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
0.7288308143615723
>>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)
0.5858950614929199

Однако обратите внимание, что timeit будет автоматически определять количество повторений только при использовании интерфейса командной строки. В разделе Примеры вы можете найти более сложные примеры.

26.6.2. Интерфейс PythonPython Interface

Модуль определяет три вспомогательные функции и один открытый класс:

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

Создаёт экземпляр Timer с заданным выражением, кодом setup и функцией timer и запускает его метод timeit() с number выполнениями.

Новое в версии 2.6.

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

Создаёт экземпляр Timer с заданным выражением, кодом setup и функцией timer и запускает его метод repeat() с указанными количеством повторов repeat и числом выполнений number.

Новое в версии 2.6.

timeit.default_timer()

Определяет таймер по умолчанию, в зависимости от платформы. На Windows time.clock() имеет микросекундную точность, а точность time.time() составляет 1/60 секунды. На Unix time.clock() имеет точность 1/100 секунды, а time.time() гораздо точнее. На обеих платформах default_timer() измеряет реальное время (wall clock), а не процессорное. Это означает, что другие процессы, выполняющиеся на том же компьютере, могут влиять на результаты измерений.

class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>)

Класс для измерения скорости выполнения небольших фрагментов кода.

Конструктор принимает измеряемое выражение, дополнительное выражение для настройки (setup) и функцию таймера. Оба выражения по умолчанию равны 'pass'; функция таймера зависит от платформы (см. документацию модуля). stmt и setup могут также содержать несколько выражений, разделённых ; или переводами строк, при условии, что они не содержат многострочных строковых литералов.

Чтобы измерить время выполнения первого оператора, используйте метод timeit(). Метод repeat() – это удобный способ вызвать timeit() несколько раз и вернуть список результатов.

Изменено в версии 2.6: Параметры stmt и setup теперь могут также принимать вызываемые объекты без аргументов. Это приведет к встраиванию вызовов этих объектов в функцию таймера, которая затем будет выполнена timeit(). Обратите внимание, что в этом случае накладные расходы на измерение немного выше из-за дополнительных вызовов функций.

timeit(number=1000000)

Измеряет время выполнения основного оператора number раз. Сначала выполняется оператор setup один раз, затем возвращается время, затраченное на выполнение основного оператора указанное количество раз, измеряемое в секундах в виде числа с плавающей запятой. Аргумент number – количество повторений цикла, по умолчанию миллион. Основной оператор, оператор setup и используемая функция таймера передаются конструктору.

Примечание

По умолчанию timeit() временно отключает сборку мусора во время замера. Преимущество такого подхода в том, что независимые замеры становятся более сопоставимыми. Недостаток в том, что сборка мусора может быть важной составляющей производительности измеряемой функции. Если это так, сборку мусора можно снова включить, указав её первой инструкцией в строке setup. Например:

timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()
repeat(repeat=3, number=1000000)

Вызов timeit() несколько раз.

Это удобная функция, которая многократно вызывает timeit(), возвращая список результатов. Первый аргумент указывает, сколько раз вызывать timeit(). Второй аргумент задаёт аргумент number для timeit().

Примечание

Может возникнуть соблазн вычислить среднее и стандартное отклонение из результирующего вектора и сообщить о них. Однако это не очень полезно. В типичном случае наименьшее значение даёт нижнюю границу того, насколько быстро ваша машина может выполнить данный фрагмент кода; более высокие значения в результирующем векторе обычно вызваны не изменчивостью скорости Python, а другими процессами, влияющими на точность измерения времени. Поэтому min() результата – вероятно, единственное число, которое должно интересовать. После этого следует посмотреть на весь вектор и применить здравый смысл, а не статистику.

print_exc(file=None)

Вспомогательная функция для вывода трассировки из кода, время выполнения которого измеряется.

Типичное использование:

t = Timer(...)       # за пределами try/except
try:
    t.timeit(...)    # или t.repeat(...)
except:
    t.print_exc()

Преимущество перед стандартной трассировкой заключается в том, что отображаются строки исходного кода из скомпилированного шаблона. Необязательный аргумент file указывает, куда отправляется трассировка; по умолчанию используется sys.stderr.

26.6.3. Интерфейс командной строкиCommand-Line Interface

При вызове как программы из командной строки используется следующая форма:

python -m timeit [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement ...]

Где поддерживаются следующие параметры:

-n N, --number=N

сколько раз выполнять 'statement'

-r N, --repeat=N

сколько раз повторять таймер (по умолчанию 3)

-s S, --setup=S

инструкция, выполняемая один раз в начале (по умолчанию pass)

-t, --time

используется time.time() (по умолчанию на всех платформах, кроме Windows)

-c, --clock

используется time.clock() (по умолчанию на Windows)

-v, --verbose

выводить необработанные результаты замера; для большей точности (больше знаков) повторите параметр

-h, --help

вывести краткое сообщение об использовании и выйти

Многострочная инструкция может быть задана указанием каждой строки как отдельного аргумента statement; строки с отступами возможны путём заключения аргумента в кавычки и использования начальных пробелов. Несколько параметров -s обрабатываются аналогично.

Если -n не указано, подходящее количество циклов вычисляется путём перебора последовательных степеней 10, пока общее время не достигнет хотя бы 0,2 секунды.

Измерения default_timer() могут зависеть от других программ, работающих на том же компьютере, поэтому при необходимости точного измерения лучше всего повторить замер несколько раз и взять наилучшее время. Опция -r подходит для этого; значения по умолчанию в 3 повторения, вероятно, достаточно в большинстве случаев. На Unix можно использовать time.clock() для измерения процессорного времени.

Примечание

Существует некоторая базовая задержка, связанная с выполнением пустого оператора pass. Код здесь не пытается её скрыть, но о ней следует знать. Базовую задержку можно измерить, вызвав программу без аргументов, и она может различаться в разных версиях Python. Кроме того, для корректного сравнения старых версий Python с Python 2.3 может потребоваться использовать опцию -O Python (см. Оптимизации) для старых версий, чтобы избежать измерения инструкций SET_LINENO.

26.6.4. ПримерыExamples

Можно указать инструкцию настройки, которая выполняется только один раз в начале:

$ python -m timeit -s 'text = "sample string"; char = "g"'  'char in text'
10000000 loops, best of 3: 0.0877 usec per loop
$ python -m timeit -s 'text = "sample string"; char = "g"'  'text.find(char)'
1000000 loops, best of 3: 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.40193588800002544, 0.3960157959998014, 0.39594301399984033]

Следующие примеры показывают, как замерять время выполнения выражений, состоящих из нескольких строк. Здесь мы сравниваем затраты на использование hasattr() против try/except для проверки отсутствующих и существующих атрибутов объектов:

$ python -m timeit 'try:' '  str.__nonzero__' 'except AttributeError:' '  pass'
100000 loops, best of 3: 15.7 usec per loop
$ python -m timeit 'if hasattr(str, "__nonzero__"): pass'
100000 loops, best of 3: 4.26 usec per loop

$ python -m timeit 'try:' '  int.__nonzero__' 'except AttributeError:' '  pass'
1000000 loops, best of 3: 1.43 usec per loop
$ python -m timeit 'if hasattr(int, "__nonzero__"): pass'
100000 loops, best of 3: 2.23 usec per loop
>>> import timeit
>>> # атрибут отсутствует
>>> s = """\
... try:
...     str.__nonzero__
... 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
>>>
>>> # атрибут присутствует
>>> s = """\
... try:
...     int.__nonzero__
... 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, содержащий инструкцию import:

def test():
    """Глупая тестовая функция"""
    L = []
    for i in range(100):
        L.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))