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

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

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

Модуль определяет следующий открытый класс:

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

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

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

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

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

Timer.print_exc(file=None)

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

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

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

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

Timer.repeat(repeat=3, number=1000000)

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

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

Примечание

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

Timer.timeit(number=1000000)

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

Примечание

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

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

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

timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=3, number=1000000)
Создаёт экземпляр Timer с указанным оператором, кодом настройки и функцией таймера и запускает его метод repeat() с заданным количеством повторений и number выполнениями.
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000)
Создаёт экземпляр Timer с указанным оператором, кодом настройки и функцией таймера и запускает его метод timeit() с number выполнениями.

26.4.1. Интерфейс командной строки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
вывести краткое сообщение об использовании и выйти

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

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

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

Примечание

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

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

26.4.2. ПримерыExamples

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

% timeit.py 'try:' '  str.__bool__' 'except AttributeError:' '  pass'
100000 loops, best of 3: 15.7 usec per loop
% timeit.py 'if hasattr(str, "__bool__"): pass'
100000 loops, best of 3: 4.26 usec per loop
% timeit.py 'try:' '  int.__bool__' 'except AttributeError:' '  pass'
1000000 loops, best of 3: 1.43 usec per loop
% timeit.py 'if hasattr(int, "__bool__"): pass'
100000 loops, best of 3: 2.23 usec per loop
>>> import timeit
>>> s = """\
... try:
...     str.__bool__
... except AttributeError:
...     pass
... """
>>> t = timeit.Timer(stmt=s)
>>> print("%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000))
17.09 usec/pass
>>> s = """\
... if hasattr(str, '__bool__'): pass
... """
>>> t = timeit.Timer(stmt=s)
>>> print("%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000))
4.85 usec/pass
>>> s = """\
... try:
...     int.__bool__
... except AttributeError:
...     pass
... """
>>> t = timeit.Timer(stmt=s)
>>> print("%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000))
1.97 usec/pass
>>> s = """\
... if hasattr(int, '__bool__'): pass
... """
>>> t = timeit.Timer(stmt=s)
>>> print("%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000))
3.15 usec/pass

Чтобы предоставить модулю timeit доступ к определённым вами функциям, вы можете передать параметр setup, содержащий инструкцию import:

def test():
    "Stupid test function"
    L = [i for i in range(100)]

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