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

timeit.md

287 строк · 21.5 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.8/library/timeit.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# [`timeit`](https://python-all.ru/3.8/library/timeit.html#module-timeit) – Измерение времени выполнения небольших фрагментов кода89**Исходный код:** [Lib/timeit.py](https://python-all.ru/src/3.8/Lib/timeit.py)1011---1213Этот модуль предоставляет простой способ измерения времени выполнения небольших фрагментов кода Python. Он имеет как [интерфейс командной строки](https://python-all.ru/3.8/library/timeit.html#timeit-command-line-interface), так и [вызываемый объект](https://python-all.ru/3.8/library/timeit.html#python-interface). Модуль избегает множества распространённых ловушек при измерении времени выполнения. См. также введение Тима Питерса к главе «Алгоритмы» в *Python Cookbook*, опубликованной O’Reilly.1415## Простые примеры1617Следующий пример показывает, как можно использовать [интерфейс командной строки](https://python-all.ru/3.8/library/timeit.html#timeit-command-line-interface) для сравнения трёх различных выражений:1819```console20$ python3 -m timeit '"-".join(str(n) for n in range(100))'2110000 loops, best of 5: 30.2 usec per loop22$ python3 -m timeit '"-".join([str(n) for n in range(100)])'2310000 loops, best of 5: 27.5 usec per loop24$ python3 -m timeit '"-".join(map(str, range(100)))'2510000 loops, best of 5: 23.2 usec per loop26```2728Этого можно добиться из [интерфейса Python](https://python-all.ru/3.8/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.8/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.8/library/timeit.html#timeit.timeit) будет автоматически определять количество повторений только при использовании интерфейса командной строки. В разделе [Примеры](https://python-all.ru/3.8/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.8/library/timeit.html#timeit.Timer) с заданными оператором, кодом *setup* и функцией *timer* и запускает его метод [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.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.8/library/timeit.html#timeit.Timer) с заданными оператором, кодом *setup* и функцией *timer* и запускает его метод [`repeat()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.repeat) с заданным количеством *repeat* и *number* выполнениями. Необязательный аргумент *globals* задаёт пространство имён, в котором будет выполняться код.6263Изменено в версии 3.5: Был добавлен необязательный параметр *globals*.6465Изменено в версии 3.7: Значение по умолчанию *repeat* изменено с 3 на 5.6667#### `timeit.default_timer()`6869Таймер по умолчанию – [`time.perf_counter()`](https://python-all.ru/3.8/library/time.html#time.perf_counter).7071Изменено в версии 3.3: [`time.perf_counter()`](https://python-all.ru/3.8/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.8/library/timeit.html#timeit.Timer.timeit). Методы [`repeat()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.repeat) и [`autorange()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.autorange) являются вспомогательными методами для многократного вызова [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit).8081Время выполнения *setup* исключается из общего времени выполнения.8283Параметры *stmt* и *setup* также могут принимать вызываемые объекты без аргументов. При этом вызовы к ним встраиваются в функцию таймера, которая затем выполняется [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit). Обратите внимание, что в этом случае накладные расходы на измерение немного больше из-за дополнительных вызовов функций.8485Изменено в версии 3.5: Был добавлен необязательный параметр *globals*.8687#### `timeit(number=1000000)`8889Измеряет время выполнения основного оператора *number* раз. Сначала выполняется оператор setup один раз, затем возвращается время, затраченное на выполнение основного оператора указанное количество раз, измеряемое в секундах в виде числа с плавающей запятой. Аргумент number – количество повторений цикла, по умолчанию миллион. Основной оператор, оператор setup и используемая функция таймера передаются конструктору.9091> **Примечание**92>93> По умолчанию [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit) временно отключает [сборку мусора](https://python-all.ru/3.8/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)`100101Автоматически определяет, сколько раз вызывать [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit).102103Это удобная функция, которая многократно вызывает [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit) так, чтобы общее время составило не менее 0,2 секунды, и возвращает итоговое значение (количество циклов, затраченное время). Она вызывает [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit) с возрастающими числами из последовательности 1, 2, 5, 10, 20, 50, … пока затраченное время не достигнет как минимум 0,2 секунды.104105Если задан *колбэк* и он не равен `None`, то он будет вызываться после каждого испытания с двумя аргументами: `callback(number, time_taken)`.106107Новое в версии 3.6.108109#### `repeat(repeat=5, number=1000000)`110111Вызов [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit) несколько раз.112113Это удобная функция, которая многократно вызывает [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit), возвращая список результатов. Первый аргумент указывает, сколько раз вызывать [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit). Второй аргумент задаёт аргумент *number* для [`timeit()`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer.timeit).114115> **Примечание**116>117> Может возникнуть соблазн вычислить среднее и стандартное отклонение из результирующего вектора и сообщить о них. Однако это не очень полезно. В типичном случае наименьшее значение даёт нижнюю границу того, насколько быстро ваша машина может выполнить данный фрагмент кода; более высокие значения в результирующем векторе обычно вызваны не изменчивостью скорости Python, а другими процессами, влияющими на точность измерения времени. Поэтому [`min()`](https://python-all.ru/3.8/library/functions.html#min) результата – вероятно, единственное число, которое должно интересовать. После этого следует посмотреть на весь вектор и применить здравый смысл, а не статистику.118119Изменено в версии 3.7: Значение по умолчанию *repeat* изменено с 3 на 5.120121#### `print_exc(file=None)`122123Вспомогательная функция для вывода трассировки из кода, время выполнения которого измеряется.124125Типичное использование:126127```python128t = Timer(...)       # за пределами try/except129try:130    t.timeit(...)    # или t.repeat(...)131except Exception:132    t.print_exc()133```134135Преимущество перед стандартной трассировкой в том, что будут отображаться строки исходного кода в скомпилированном шаблоне. Необязательный аргумент *file* указывает, куда направляется трассировка; по умолчанию используется [`sys.stderr`](https://python-all.ru/3.8/library/sys.html#sys.stderr).136137## Интерфейс командной строки138139При вызове как программы из командной строки используется следующая форма:140141```python142python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...]143```144145Где поддерживаются следующие параметры:146147#### `-n N, --number=N`148149сколько раз выполнять 'statement'150151#### `-r N, --repeat=N`152153сколько раз повторить таймер (по умолчанию 5)154155#### `-s S, --setup=S`156157инструкция, выполняемая один раз в начале (по умолчанию `pass`)158159#### `-p, --process`160161измерять процессорное время, а не настенное время, используя [`time.process_time()`](https://python-all.ru/3.8/library/time.html#time.process_time) вместо [`time.perf_counter()`](https://python-all.ru/3.8/library/time.html#time.perf_counter), которое используется по умолчанию162163Новое в версии 3.3.164165#### `-u, --unit=U`166167указывает единицу времени для вывода таймера; можно выбрать nsec, usec, msec или sec168169Новое в версии 3.5.170171#### `-v, --verbose`172173выводить необработанные результаты замера; для большей точности (больше знаков) повторите параметр174175#### `-h, --help`176177вывести краткое сообщение об использовании и выйти178179Многострочная инструкция может быть задана указанием каждой строки как отдельного аргумента statement; строки с отступами возможны путём заключения аргумента в кавычки и использования начальных пробелов. Несколько параметров [`-s`](https://python-all.ru/3.8/library/timeit.html#cmdoption-timeit-s) обрабатываются аналогично.180181Если [`-n`](https://python-all.ru/3.8/library/timeit.html#cmdoption-timeit-n) не задан, подходящее количество циклов вычисляется путём проб возрастающих чисел из последовательности 1, 2, 5, 10, 20, 50, … пока общее время не достигнет хотя бы 0.2 секунды.182183На результаты измерений [`default_timer()`](https://python-all.ru/3.8/library/timeit.html#timeit.default_timer) могут влиять другие программы, выполняющиеся на той же машине, поэтому при необходимости точных замеров лучше всего повторить измерение несколько раз и взять наилучшее время. Параметр [`-r`](https://python-all.ru/3.8/library/timeit.html#cmdoption-timeit-r) подходит для этого; значения по умолчанию 5 повторений, вероятно, достаточно в большинстве случаев. Можно использовать [`time.process_time()`](https://python-all.ru/3.8/library/time.html#time.process_time) для измерения процессорного времени.184185> **Примечание**186>187> Существует некоторая базовая накладная нагрузка, связанная с выполнением инструкции pass. Приведённый здесь код не пытается её скрыть, но о ней следует знать. Базовую накладную нагрузку можно измерить, вызвав программу без аргументов, и она может отличаться в разных версиях Python.188189## Примеры190191Можно указать инструкцию настройки, которая выполняется только один раз в начале:192193```console194$ python -m timeit -s 'text = "sample string"; char = "g"'  'char in text'1955000000 loops, best of 5: 0.0877 usec per loop196$ python -m timeit -s 'text = "sample string"; char = "g"'  'text.find(char)'1971000000 loops, best of 5: 0.342 usec per loop198```199200```python201>>> import timeit202>>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')2030.41440500499993504204>>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')2051.7246671520006203206```207208То же самое можно сделать с помощью класса [`Timer`](https://python-all.ru/3.8/library/timeit.html#timeit.Timer) и его методов:209210```python211>>> import timeit212>>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')213>>> t.timeit()2140.3955516149999312215>>> t.repeat()216[0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]217```218219Следующие примеры показывают, как замерять время выполнения выражений, состоящих из нескольких строк. Здесь мы сравниваем затраты на использование [`hasattr()`](https://python-all.ru/3.8/library/functions.html#hasattr) против [`try`](https://python-all.ru/3.8/reference/compound_stmts.html#try)/[`except`](https://python-all.ru/3.8/reference/compound_stmts.html#except) для проверки отсутствующих и существующих атрибутов объектов:220221```console222$ python -m timeit 'try:' '  str.__bool__' 'except AttributeError:' '  pass'22320000 loops, best of 5: 15.7 usec per loop224$ python -m timeit 'if hasattr(str, "__bool__"): pass'22550000 loops, best of 5: 4.26 usec per loop226227$ python -m timeit 'try:' '  int.__bool__' 'except AttributeError:' '  pass'228200000 loops, best of 5: 1.43 usec per loop229$ python -m timeit 'if hasattr(int, "__bool__"): pass'230100000 loops, best of 5: 2.23 usec per loop231```232233```python234>>> import timeit235>>> # атрибут отсутствует236>>> s = """\237... try:238...     str.__bool__239... except AttributeError:240...     pass241... """242>>> timeit.timeit(stmt=s, number=100000)2430.9138244460009446244>>> s = "if hasattr(str, '__bool__'): pass"245>>> timeit.timeit(stmt=s, number=100000)2460.5829014980008651247>>>248>>> # атрибут присутствует249>>> s = """\250... try:251...     int.__bool__252... except AttributeError:253...     pass254... """255>>> timeit.timeit(stmt=s, number=100000)2560.04215312199994514257>>> s = "if hasattr(int, '__bool__'): pass"258>>> timeit.timeit(stmt=s, number=100000)2590.08588060699912603260```261262Чтобы предоставить модулю [`timeit`](https://python-all.ru/3.8/library/timeit.html#module-timeit) доступ к определяемым вами функциям, можно передать параметр *setup*, содержащий инструкцию import:263264```python265def test():266    """Глупая тестовая функция"""267    L = [i for i in range(100)]268269if __name__ == '__main__':270    import timeit271    print(timeit.timeit("test()", setup="from __main__ import test"))272```273274Другой вариант – передать [`globals()`](https://python-all.ru/3.8/library/functions.html#globals) параметру *globals*, что приведёт к выполнению кода в текущем глобальном пространстве имён. Это может быть удобнее, чем указывать импорты по отдельности:275276```python277def f(x):278    return x**2279def g(x):280    return x**4281def h(x):282    return x**8283284import timeit285print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))286```287