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

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


Модуль предоставляет простой способ определения времени малых битов Python код. Имеет как Интерфейс командной строки, так и callable. Он позволяет избежать ряда обычных ловушек для измерения времени выполнения. См. также введение Tim Peters’а в главе «Алгоритмы» в 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 сущность с заданной функцией инструкция, 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().

Изменено в версии 3.3: time.perf_counter() теперь является таймером по умолчанию.

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

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

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

Для измерения времени выполнения первой инструкции используйте метод timeit(). Методы repeat() и autorange() являются удобными способами многократного вызова timeit().

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

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

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

timeit(number=1000000)

Время number расстрелов главных инструкция. При этом инструкция установки выполняется один раз, а затем возвращает время, необходимое для выполнения основного инструкция, несколько раз, измеренное в секундах как float. Аргумент - это число раз в цикле, по умолчанию равное одному миллиону. Главный инструкция, инструкция установки и используемый функция таймера передаются конструктору.

Примечание

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

сколько раз выполнить инструкцию

-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().

Примечание

С выполнением инструкция передачи связаны определенные накладные расходы базовой линии. Здесь код не пытается скрыть, но вы должны знать об этом. Базовые издержки могут быть измерены путем вызова программы без аргументов, и они могут отличаться между 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()))