Spec-Zone.ru › Python 3.5

timeit — Измерение времени выполнения небольших фрагментов кода

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

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

1. Базовые примеры

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

$ python3 -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 30.2 usec per loop
$ python3 -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 27.5 usec per loop
$ python3 -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 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

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

2. Интерфейс 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=3, number=1000000, globals=None)

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

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

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() является удобным способом вызова timeit() несколько раз и возвращает список результатов.

Время выполнения setup не учитывается в общем измерении времени выполнения.

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

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

timeit(number=1000000)

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

Примечание

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

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

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

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

Примечание

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

print_exc(file=None)

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

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

t = Timer(...)       # outside the try/except
try:
    t.timeit(...)    # or t.repeat(...)
except Exception:
    t.print_exc()

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

3. Командная строка

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

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

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

-n N, --number=N

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

-r N, --repeat=N

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

-s S, --setup=S

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

-p, --process

измерить время обработки, а не время wallclock, используя time.process_time() вместо time.perf_counter(), что является значением по умолчанию

Добавлена в версии 3.3.

-t, --time

использовать time.time() (устарело)

-u, --unit=U
задать единицу времени для вывода таймера; можно выбрать usec, msec или sec

Добавлена в версии 3.5.

-c, --clock

использовать time.clock() (устарело)

-v, --verbose

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

-h, --help

вывести короткое сообщение об использовании и завершить программу

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

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

END_OF_DOCUMENT_MARKER

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

Примечание

Существует определённая базовая накладная стоимость, связанная с выполнением инструкции pass. В данном примере код не пытается её скрыть, но вы должны быть осведомлены об этом. Базовую накладную стоимость можно измерить, вызвав программу без аргументов, и она может отличаться между версиями Python.

4. Примеры

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

$ 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.__bool__' 'except AttributeError:' '  pass'
100000 loops, best of 3: 15.7 usec per loop
$ python -m timeit 'if hasattr(str, "__bool__"): pass'
100000 loops, best of 3: 4.26 usec per loop

$ python -m timeit 'try:' '  int.__bool__' 'except AttributeError:' '  pass'
1000000 loops, best of 3: 1.43 usec per loop
$ python -m timeit 'if hasattr(int, "__bool__"): pass'
100000 loops, best of 3: 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–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/timeit.html

Spec-Zone.ru

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