Spec-Zone.ru › Python 3.7

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

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

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

Основные примеры

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

$ python3 -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 5: 30.2 usec per loop
$ python3 -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 5: 27.5 usec per loop
$ python3 -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 с заданным оператором, кодом подготовки и функцией таймера, и выполняет метод timeit() с заданным числом выполнений. Необязательный аргумент globals задаёт пространство имён, в котором будет выполняться код.

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

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

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

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

Изменено в версии 3.7: Значение по умолчанию для repeat изменено с 3 на 5.

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() временно отключает сборку мусора во время измерения. Преимущество этого подхода заключается в том, что независимые измерения становятся более сопоставимыми. Недостаток заключается в том, что сборка мусора может быть важной частью производительности измеряемой функции. Если это так, сборку мусора можно включить в качестве первого оператора в строке 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] [-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.

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

Spec-Zone.ru

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