timeit.md
1> **Источник:** https://python-all.ru/3.16/library/timeit.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `timeit` – Измерение времени выполнения небольших фрагментов кода89**Исходный код:** [Lib/timeit.py](https://python-all.ru/src/main/Lib/timeit.py)1011---1213Этот модуль предоставляет простой способ измерения времени выполнения небольших фрагментов кода Python. Он имеет как [интерфейс командной строки](https://python-all.ru/3.16/library/timeit.html#timeit-command-line-interface), так и [вызываемый интерфейс](https://python-all.ru/3.16/library/timeit.html#python-interface). Он позволяет избежать ряда типичных ловушек при измерении времени выполнения.\\nСм. также введение Тима Питерса к главе «Алгоритмы» во втором издании *Python Cookbook*, опубликованном O’Reilly.1415## Базовые примеры1617В следующем примере показано, как [интерфейс командной строки](https://python-all.ru/3.16/library/timeit.html#timeit-command-line-interface)\\nможно использовать для сравнения трёх различных выражений:1819```console20$ python -m timeit "'-'.join(str(n) for n in range(100))"2110000 loops, best of 5: 30.2 usec per loop22$ python -m timeit "'-'.join([str(n) for n in range(100)])"2310000 loops, best of 5: 27.5 usec per loop24$ python -m timeit "'-'.join(map(str, range(100)))"2510000 loops, best of 5: 23.2 usec per loop26```2728Этого можно добиться через [интерфейс Python](https://python-all.ru/3.16/library/timeit.html#python-interface) с помощью:2930```python31>>> import timeit32>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)330.301861184882000134>>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)350.272736879065632836>>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)370.2370267980732023738```3940Вызываемый объект также можно передать через [интерфейс Python](https://python-all.ru/3.16/library/timeit.html#python-interface):4142```python43>>> timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000)440.1966559197753667845```4647Однако обратите внимание, что [`timeit()`](https://python-all.ru/3.16/library/timeit.html#timeit.timeit) будет автоматически определять количество повторений только при использовании интерфейса командной строки. В разделе [Примеры](https://python-all.ru/3.16/library/timeit.html#timeit-examples) вы можете найти более сложные примеры.4849## Интерфейс Python5051Модуль определяет три вспомогательные функции и один открытый класс:5253#### `timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)`5455Создаёт экземпляр [`Timer`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer) с заданными оператором, кодом *setup* и функцией *timer* и запускает его метод `timeit()` с *number* выполнениями. Необязательный аргумент *globals* задаёт пространство имён, в котором будет выполняться код.5657Изменено в версии 3.5: Был добавлен необязательный параметр *globals*.5859#### `timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)`6061Создаёт экземпляр [`Timer`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer) с заданными оператором, кодом *setup* и функцией *timer* и запускает его метод `repeat()` с заданным количеством *repeat* и *number* выполнениями. Необязательный аргумент *globals* задаёт пространство имён, в котором будет выполняться код.6263Изменено в версии 3.5: Был добавлен необязательный параметр *globals*.6465Изменено в версии 3.7: Значение по умолчанию *repeat* изменено с 3 на 5.6667#### `timeit.default_timer()`6869Таймер по умолчанию, который всегда равен time.perf\_counter(), возвращает число секунд с плавающей точкой. Альтернатива time.perf\_counter\_ns возвращает целое число наносекунд.7071Изменено в версии 3.3: [`time.perf_counter()`](https://python-all.ru/3.16/library/time.html#time.perf_counter) теперь является таймером по умолчанию.7273#### `class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)`7475Класс для измерения скорости выполнения небольших фрагментов кода.7677Конструктор принимает измеряемый оператор, дополнительный оператор для настройки и функцию таймера. Оба оператора по умолчанию равны `'pass'`; функция таймера зависит от платформы (см. строку документации модуля). *stmt* и *setup* также могут содержать несколько операторов, разделённых `;` или переводами строк, при условии, что они не содержат многострочных строковых литералов. По умолчанию оператор выполняется в пространстве имён timeit; это поведение можно контролировать, передав пространство имён в *globals*.7879Для измерения времени выполнения первого оператора используйте метод [`timeit()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.timeit). Методы [`repeat()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.repeat) и [`autorange()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.autorange) являются вспомогательными методами для многократного вызова `timeit()`.8081Время выполнения *setup* исключается из общего времени выполнения.8283Параметры *stmt* и *setup* также могут принимать вызываемые объекты без аргументов. При этом вызовы к ним встраиваются в функцию таймера, которая затем выполняется [`timeit()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.timeit). Обратите внимание, что в этом случае накладные расходы на измерение немного больше из-за дополнительных вызовов функций.8485Изменено в версии 3.5: Был добавлен необязательный параметр *globals*.8687#### `timeit(number=1000000)`8889Замеряет *number* выполнений основного оператора. Сначала один раз выполняет оператор настройки, а затем возвращает время, затраченное на несколько выполнений основного оператора. Таймер по умолчанию возвращает число секунд с плавающей точкой. Аргумент – количество проходов цикла, по умолчанию один миллион. Основной оператор, оператор настройки и используемая функция таймера передаются конструктору.9091> **Примечание**92>93> По умолчанию `timeit()` временно отключает [сборку мусора](https://python-all.ru/3.16/glossary.html#term-garbage-collection) во время замера. Преимущество такого подхода в том, что независимые замеры становятся более сопоставимыми. Недостаток в том, что сборка мусора может быть важной составляющей производительности измеряемой функции. Если это так, сборку мусора можно снова включить, указав её первой инструкцией в строке *setup*. Например:94>95> ```python96> timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()97> ```9899#### `autorange(callback=None, target_time=None)`100101Автоматически определяет, сколько раз вызывать [`timeit()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.timeit).102103Это удобная функция, которая многократно вызывает [`timeit()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.timeit) так, чтобы общее время \>= *Timer.target\_time* секунд, и возвращает результирующие (количество циклов, затраченное время для этого количества циклов). Она вызывает `timeit()` с возрастающими числами из последовательности 1, 2, 5, 10, 20, 50, … до тех пор, пока затраченное время не станет не менее *target\_time* секунд.104105Если задан *колбэк* и он не равен `None`, то он будет вызываться после каждого испытания с двумя аргументами: `callback(number, time_taken)`.106107Добавлено в версии 3.6.108109Изменено в версии 3.15: Добавлен необязательный параметр *target\_time*.110111#### `repeat(repeat=5, number=1000000)`112113Вызов [`timeit()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.timeit) несколько раз.114115Это удобная функция, которая многократно вызывает [`timeit()`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer.timeit), возвращая список результатов. Первый аргумент указывает, сколько раз вызывать `timeit()`. Второй аргумент задаёт аргумент *number* для `timeit()`.116117> **Примечание**118>119> Может возникнуть соблазн вычислить среднее и стандартное отклонение из результирующего вектора и сообщить о них. Однако это не очень полезно. В типичном случае наименьшее значение даёт нижнюю границу того, насколько быстро ваша машина может выполнить данный фрагмент кода; более высокие значения в результирующем векторе обычно вызваны не изменчивостью скорости Python, а другими процессами, влияющими на точность измерения времени. Поэтому [`min()`](https://python-all.ru/3.16/library/functions.html#min) результата – вероятно, единственное число, которое должно интересовать. После этого следует посмотреть на весь вектор и применить здравый смысл, а не статистику.120121Изменено в версии 3.7: Значение по умолчанию *repeat* изменено с 3 на 5.122123#### `print_exc(file=None)`124125Вспомогательная функция для вывода трассировки из кода, время выполнения которого измеряется.126127Типичное использование:128129```python130t = Timer(...) # за пределами try/except131try:132 t.timeit(...) # or t.repeat(...)133except Exception:134 t.print_exc()135```136137Преимущество перед стандартной трассировкой в том, что будут отображаться строки исходного кода в скомпилированном шаблоне. Необязательный аргумент *file* указывает, куда направляется трассировка; по умолчанию используется [`sys.stderr`](https://python-all.ru/3.16/library/sys.html#sys.stderr).138139## Интерфейс командной строки140141При вызове как программы из командной строки используется следующая форма:142143```python144python -m timeit [-n N] [-r N] [-u U] [-s S] [-p] [-v] [-h] [statement ...]145```146147Где поддерживаются следующие параметры:148149#### `-n N, --number=N`150151сколько раз выполнять 'statement'152153#### `-r N, --repeat=N`154155сколько раз повторить таймер (по умолчанию 5)156157#### `-s S, --setup=S`158159инструкция, выполняемая один раз в начале (по умолчанию `pass`)160161#### `-p, --process`162163измерять процессорное время, а не настенное время, используя [`time.process_time()`](https://python-all.ru/3.16/library/time.html#time.process_time) вместо [`time.perf_counter()`](https://python-all.ru/3.16/library/time.html#time.perf_counter), которое используется по умолчанию164165Добавлено в версии 3.3.166167#### `-u, --unit=U`168169указать единицу времени для вывода таймера; можно выбрать `nsec`, `usec`, `msec` или `sec`170171Добавлено в версии 3.5.172173#### `-t, --target-time=T`174175Если [`--number`](https://python-all.ru/3.16/library/timeit.html#cmdoption-timeit-n) равен 0, код будет выполняться до тех пор, пока не займёт как минимум это количество секунд (по умолчанию: 0.2).176177Добавлено в версии 3.15.178179#### `-v, --verbose`180181выводить необработанные результаты замера; для большей точности (больше знаков) повторите параметр182183#### `-h, --help`184185вывести краткое сообщение об использовании и выйти186187Многострочная инструкция может быть задана указанием каждой строки как отдельного аргумента statement; строки с отступами возможны путём заключения аргумента в кавычки и использования начальных пробелов. Несколько параметров [`-s`](https://python-all.ru/3.16/library/timeit.html#cmdoption-timeit-s) обрабатываются аналогично.188189Если [`-n`](https://python-all.ru/3.16/library/timeit.html#cmdoption-timeit-n) не указан, подходящее количество циклов вычисляется путём перебора возрастающих чисел из последовательности 1, 2, 5, 10, 20, 50, … до тех пор, пока общее время не станет не менее [`--target-time`](https://python-all.ru/3.16/library/timeit.html#cmdoption-timeit-t) секунд (по умолчанию: 0.2).190191На результаты измерений [`default_timer()`](https://python-all.ru/3.16/library/timeit.html#timeit.default_timer) могут влиять другие программы, выполняющиеся на той же машине, поэтому при необходимости точных замеров лучше всего повторить измерение несколько раз и взять наилучшее время. Параметр [`-r`](https://python-all.ru/3.16/library/timeit.html#cmdoption-timeit-r) подходит для этого; значения по умолчанию 5 повторений, вероятно, достаточно в большинстве случаев. Можно использовать [`time.process_time()`](https://python-all.ru/3.16/library/time.html#time.process_time) для измерения процессорного времени.192193> **Примечание**194>195> Существует некоторая базовая накладная нагрузка, связанная с выполнением инструкции pass. Приведённый здесь код не пытается её скрыть, но о ней следует знать. Базовую накладную нагрузку можно измерить, вызвав программу без аргументов, и она может отличаться в разных версиях Python.196197Добавлено в версии 3.15: Вывод по умолчанию цветной, и его можно [настроить с помощью переменных окружения](https://python-all.ru/3.16/using/cmdline.html#using-on-controlling-color).198199## Примеры200201Можно указать инструкцию настройки, которая выполняется только один раз в начале:202203```console204$ python -m timeit -s "text = 'sample string'; char = 'g'" "char in text"2055000000 loops, best of 5: 0.0877 usec per loop206$ python -m timeit -s "text = 'sample string'; char = 'g'" "text.find(char)"2071000000 loops, best of 5: 0.342 usec per loop208```209210В выводе представлены три поля. Количество итераций цикла (loop count), показывающее, сколько раз тело оператора выполнялось за одно повторение цикла замера. Количество повторений (repetition count, «best of 5»), показывающее, сколько раз цикл замера повторялся, и, наконец, среднее время выполнения тела оператора в лучшем повторении цикла замера. То есть время, затраченное самым быстрым повторением, делённое на количество итераций цикла.211212```python213>>> import timeit214>>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')2150.41440500499993504216>>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')2171.7246671520006203218```219220То же самое можно сделать с помощью класса [`Timer`](https://python-all.ru/3.16/library/timeit.html#timeit.Timer) и его методов:221222```python223>>> import timeit224>>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')225>>> t.timeit()2260.3955516149999312227>>> t.repeat()228[0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]229```230231Следующие примеры показывают, как замерять время выполнения выражений, состоящих из нескольких строк. Здесь мы сравниваем затраты на использование [`hasattr()`](https://python-all.ru/3.16/library/functions.html#hasattr) против [`try`](https://python-all.ru/3.16/reference/compound_stmts.html#try)/[`except`](https://python-all.ru/3.16/reference/compound_stmts.html#except) для проверки отсутствующих и существующих атрибутов объектов:232233```console234$ python -m timeit "try:" " str.__bool__" "except AttributeError:" " pass"23520000 loops, best of 5: 15.7 usec per loop236$ python -m timeit "if hasattr(str, '__bool__'): pass"23750000 loops, best of 5: 4.26 usec per loop238239$ python -m timeit "try:" " int.__bool__" "except AttributeError:" " pass"240200000 loops, best of 5: 1.43 usec per loop241$ python -m timeit "if hasattr(int, '__bool__'): pass"242100000 loops, best of 5: 2.23 usec per loop243```244245```python246>>> import timeit247>>> # атрибут отсутствует248>>> s = """\249... try:250... str.__bool__251... except AttributeError:252... pass253... """254>>> timeit.timeit(stmt=s, number=100000)2550.9138244460009446256>>> s = "if hasattr(str, '__bool__'): pass"257>>> timeit.timeit(stmt=s, number=100000)2580.5829014980008651259>>>260>>> # атрибут присутствует261>>> s = """\262... try:263... int.__bool__264... except AttributeError:265... pass266... """267>>> timeit.timeit(stmt=s, number=100000)2680.04215312199994514269>>> s = "if hasattr(int, '__bool__'): pass"270>>> timeit.timeit(stmt=s, number=100000)2710.08588060699912603272```273274Чтобы предоставить модулю `timeit` доступ к определяемым вами функциям, можно передать параметр *setup*, содержащий инструкцию import:275276```python277def test():278 """Глупая тестовая функция"""279 L = [i for i in range(100)]280281if __name__ == '__main__':282 import timeit283 print(timeit.timeit("test()", setup="from __main__ import test"))284```285286Другой вариант – передать [`globals()`](https://python-all.ru/3.16/library/functions.html#globals) параметру *globals*, что приведёт к выполнению кода в текущем глобальном пространстве имён. Это может быть удобнее, чем указывать импорты по отдельности:287288```python289def f(x):290 return x**2291def g(x):292 return x**4293def h(x):294 return x**8295296import timeit297print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))298```299