Spec-Zone.ru › Python 3.6

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

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

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

27.5.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 автоматически определит количество повторений только при использовании интерфейса командной строки. В разделе Примеры вы найдёте более сложные примеры.

27.5.2. Интерфейс Python

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

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

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

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

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

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

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

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

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

timeit(number=1000000)

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

Примечание

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

timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
autorange(callback=None)

Автоматически определяет, сколько раз нужно вызывать timeit().

Это удобная функция, которая многократно вызывает timeit() таким образом, что общее время >= 0,2 секунды, возвращая конечное (количество циклов, время, затраченное на это количество циклов). Она вызывает timeit() со значениями number, равными последовательным степеням десяти (10, 100, 1000, …) до максимального значения в один миллиард, пока время выполнения не станет не менее 0,2 секунды или не будет достигнута максимальная граница.

Если задан callback и он не None, он будет вызываться после каждой пробы с двумя аргументами: callback(number, time_taken).

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

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.

27.5.3. Интерфейс командной строки

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

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

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

-n N, --number=N

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

-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 секунды.

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

Примечание

Есть определённые базовые накладные расходы, связанные с выполнением оператора pass. Код здесь не пытается их скрыть, но вы должны быть об этом осведомлены. Базовые накладные расходы можно измерить, вызвав программу без аргументов, и они могут отличаться между версиями Python.

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

Spec-Zone.ru

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