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

profile.md

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

1> **Источник:** https://python-all.ru/3.16/library/profile.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `profile` – Профилировщик на чистом Python89**Исходный код:** [Lib/profile.py](https://python-all.ru/src/main/Lib/profile.py)1011---1213Устарело с версии 3.15, будет удалено в версии 3.17.1415Модуль `profile` устарел и будет удалён в Python 3.17. Используйте вместо него [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing).1617Модуль `profile` содержит реализацию детерминированного профилировщика на чистом Python. Хотя он может пригодиться для изучения внутреннего устройства профилировщика или расширения его поведения через наследование, его реализация на чистом Python вносит значительные накладные расходы по сравнению с C-модулем [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing).1819Для большинства задач профилирования используйте:2021- [`profiling.sampling`](https://python-all.ru/3.16/library/profiling.sampling.html#module-profiling.sampling) для отладки в продакшене с нулевыми накладными расходами22- [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing) для разработки и тестирования2324## Миграция2526Переход с `profile` на [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing) не представляет сложности. API совместимы:2728```python29# Старое (устарело)30import profile31profile.run('my_function()')3233# Новое (рекомендуется)34import profiling.tracing35profiling.tracing.run('my_function()')36```3738В большинстве случаев достаточно заменить `import profile` на `import profiling.tracing` (и использовать `profiling.tracing` вместо `profile` по всему коду) – это прямой путь миграции.3940> **Примечание**41>42> Модуль `cProfile` остаётся доступным как обратно совместимый псевдоним [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing). Существующий код, использующий `import cProfile`, продолжит работу без изменений.4344## `profile` и `profiling.tracing` – справочник по модулям4546Оба модуля, `profile` и [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing), предоставляют следующие функции:4748#### `profile.run(command, filename=None, sort=-1)`4950Эта функция принимает один аргумент, который можно передать функции [`exec()`](https://python-all.ru/3.16/library/functions.html#exec), и необязательное имя файла. Во всех случаях эта процедура выполняет:5152```python53exec(command, __main__.__dict__, __main__.__dict__)54```5556и собирает статистику профилирования из выполнения. Если имя файла не указано, эта функция автоматически создаёт экземпляр [`Stats`](https://python-all.ru/3.16/library/pstats.html#pstats.Stats) и выводит простой отчёт профилирования. Если указано значение сортировки, оно передаётся этому экземпляру `Stats` для управления сортировкой результатов.5758#### `profile.runctx(command, globals, locals, filename=None, sort=-1)`5960Эта функция похожа на [`run()`](https://python-all.ru/3.16/library/profile.html#profile.run), но с дополнительными аргументами для указания словарей глобальных и локальных переменных для строки *команда*. Эта процедура выполняет:6162```python63exec(command, globals, locals)64```6566и собирает статистику профилирования, как в функции [`run()`](https://python-all.ru/3.16/library/profile.html#profile.run) выше.6768#### `class profile.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)`6970Этот класс обычно используется только тогда, когда требуется более точный контроль профилирования, чем предоставляет функция [`profiling.tracing.run()`](https://python-all.ru/3.16/library/profiling.tracing.html#profiling.tracing.run).7172Можно указать собственный таймер для измерения времени выполнения кода с помощью аргумента *timer*. Это должна быть функция, возвращающая одно число, представляющее текущее время. Если число целое, *timeunit* задаёт множитель, определяющий длительность каждой единицы времени. Например, если таймер возвращает время, измеряемое в тысячных долях секунды, единица времени будет `.001`.7374Непосредственное использование класса `Profile` позволяет форматировать результаты профилирования без записи данных профиля в файл:7576```python77import profiling.tracing78import pstats79import io80from pstats import SortKey8182pr = profiling.tracing.Profile()83pr.enable()84# ... сделать что-то ...85pr.disable()86s = io.StringIO()87sortby = SortKey.CUMULATIVE88ps = pstats.Stats(pr, stream=s).sort_stats(sortby)89ps.print_stats()90print(s.getvalue())91```9293Класс `Profile` также можно использовать как контекстный менеджер (поддерживается только в [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing), но не в устаревшем модуле `profile`; см. [Типы контекстных менеджеров](https://python-all.ru/3.16/library/stdtypes.html#typecontextmanager)):9495```python96import profiling.tracing9798with profiling.tracing.Profile() as pr:99    # ... сделать что-то ...100101    pr.print_stats()102```103104Изменено в версии 3.8: Добавлена поддержка контекстного менеджера.105106#### `enable()`107108Начать сбор данных профилирования. Только в [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing).109110#### `disable()`111112Остановить сбор данных профилирования. Только в [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing).113114#### `create_stats()`115116Остановить сбор данных профилирования и сохранить результаты внутри как текущий профиль.117118#### `print_stats(sort=-1)`119120Создать объект [`Stats`](https://python-all.ru/3.16/library/pstats.html#pstats.Stats) на основе текущего профиля и вывести результаты в stdout.121122Параметр *sort* задаёт порядок сортировки отображаемой статистики. Он принимает один ключ или кортеж ключей для многоуровневой сортировки, как в [`pstats.Stats.sort_stats()`](https://python-all.ru/3.16/library/pstats.html#pstats.Stats.sort_stats).123124Добавлено в версии 3.13: `print_stats()` теперь принимает кортеж ключей.125126#### `dump_stats(filename)`127128Записать результаты текущего профиля в *filename*.129130#### `run(cmd)`131132Профилировать cmd с помощью [`exec()`](https://python-all.ru/3.16/library/functions.html#exec).133134#### `runctx(cmd, globals, locals)`135136Профилировать cmd с помощью [`exec()`](https://python-all.ru/3.16/library/functions.html#exec) с указанным глобальным и локальным окружением.137138#### `runcall(func, /, *args, **kwargs)`139140Профилировать `func(*args, **kwargs)`141142Обратите внимание, что профилирование будет работать, только если вызванная команда/функция действительно возвращает управление. Если интерпретатор завершается (например, вызовом [`sys.exit()`](https://python-all.ru/3.16/library/sys.html#sys.exit) во время выполнения вызванной команды/функции), результаты профилирования не будут выведены.143144## Отличия от `profiling.tracing`145146Модуль `profile` отличается от [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing) в нескольких аспектах:147148**Бо́льшие накладные расходы.** Реализация на чистом Python существенно медленнее C-реализации, поэтому она не подходит для профилирования долго работающих программ или кода, чувствительного к производительности.149150**Поддержка калибровки.** Модуль `profile` поддерживает калибровку для компенсации накладных расходов профилирования. В [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing) она не нужна, потому что C-реализация даёт пренебрежимо малые накладные расходы.151152**Пользовательские таймеры.** Оба модуля поддерживают пользовательские таймеры, но `profile` принимает функции-таймеры, возвращающие кортежи (например, [`os.times()`](https://python-all.ru/3.16/library/os.html#os.times)), а [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing) требует функцию, возвращающую одно число.153154**Создание подклассов.** Чистая реализация на Python упрощает создание подклассов и расширение для настройки поведения профилирования.155156## Что такое детерминированное профилирование?157158*Детерминированное профилирование* означает, что отслеживаются все события *вызова функции*, *возврата из функции* и *исключения*, и выполняются точные замеры времени для интервалов между этими событиями (в течение которых выполняется пользовательский код). В отличие от этого, *статистическое профилирование* (которое предоставляется модулем [`profiling.sampling`](https://python-all.ru/3.16/library/profiling.sampling.html#module-profiling.sampling)) периодически сэмплирует актуальный указатель инструкций и выводит предположения о том, где тратится время. Последний метод традиционно имеет меньшие накладные расходы (поскольку код не требует инструментирования), но даёт лишь относительные указания на то, где расходуется время.159160В Python, поскольку во время выполнения активен интерпретатор, для детерминированного профилирования не требуется инструментированный код. Python автоматически предоставляет *ловушку (хук)* (необязательный колбэк) для каждого события. Кроме того, интерпретируемый характер Python добавляет столько накладных расходов на выполнение, что детерминированное профилирование в типичных приложениях добавляет лишь небольшие вычислительные накладные расходы. В результате детерминированное профилирование не так затратно, но при этом предоставляет обширную статистику времени выполнения программы Python.161162Статистику количества вызовов можно использовать для выявления ошибок в коде (неожиданные количества) и для определения возможных точек встраивания (высокое количество вызовов). Статистику внутреннего времени можно использовать для выявления «горячих циклов», которые следует тщательно оптимизировать. Статистику суммарного времени следует использовать для выявления высокоуровневых ошибок в выборе алгоритмов. Обратите внимание, что нестандартная обработка суммарного времени в этом профилировщике позволяет напрямую сравнивать статистику рекурсивных реализаций алгоритмов с итеративными.163164## Ограничения165166Одно ограничение связано с точностью информации о времени. Существует фундаментальная проблема с детерминированными профилировщиками, касающаяся точности. Наиболее очевидное ограничение состоит в том, что внутренние «часы» тикают с частотой (обычно) около 0,001 секунды. Следовательно, ни одно измерение не может быть точнее этих часов. Если сделать достаточно измерений, «ошибка» будет стремиться к усреднению. К сожалению, устранение этой первой ошибки приводит ко второму источнику ошибок.167168The second problem is that it “takes a while” from when an event is dispatched until the profiler’s call to get the time actually *gets* the state of the clock. Similarly, there is a certain lag when exiting the profiler event handler from the time that the clock’s value was obtained (and then squirreled away), until the user’s code is once again executing. As a result, functions that are called many times, or call many functions, will typically accumulate this error. The error that accumulates in this fashion is typically less than the accuracy of the clock (less than one clock tick), but it *can* accumulate and become very significant.169170Проблема более значима для устаревшего модуля `profile`, чем для модуля с меньшими накладными расходами [`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing). По этой причине `profile` предоставляет возможность калибровки для конкретной платформы, чтобы эту ошибку можно было вероятностно (в среднем) устранить. После калибровки профилировщик станет точнее (в смысле наименьших квадратов), но иногда будет выдавать отрицательные числа (когда количество вызовов исключительно мало, и боги вероятности не на вашей стороне :-)). *Не* пугайтесь отрицательных чисел в профиле. Они должны *только* появляться, если вы откалибровали профилировщик, и результаты на самом деле лучше, чем без калибровки.171172## Калибровка173174Профилировщик модуля `profile` вычитает константу из времени обработки каждого события, чтобы компенсировать накладные расходы на вызов функции времени и сохранение результатов. По умолчанию константа равна 0. Следующую процедуру можно использовать для получения лучшей константы для данной платформы (см. [Ограничения](https://python-all.ru/3.16/library/profile.html#profile-limitations)).175176```python177import profile178pr = profile.Profile()179for i in range(5):180    print(pr.calibrate(10000))181```182183Метод выполняет количество вызовов Python, заданное аргументом, напрямую и снова под профилировщиком, измеряя время для обоих случаев. Затем он вычисляет скрытые накладные расходы на одно событие профилировщика и возвращает это значение в виде числа с плавающей запятой. Например, на процессоре Intel Core i5 с частотой 1,8 ГГц под управлением macOS и с использованием time.process\_time() в Python в качестве таймера магическое число составляет около 4,04e-6.184185Цель этого упражнения – получить достаточно стабильный результат. Если ваш компьютер *очень* быстрый или функция таймера имеет низкое разрешение, возможно, придётся передать 100000 или даже 1000000, чтобы получить стабильные результаты.186187Когда получен стабильный результат, его можно использовать тремя способами:188189```python190import profile191192# 1. Применить вычисленное смещение ко всем экземплярам Profile, создаваемым в дальнейшем.193profile.Profile.bias = your_computed_bias194195# 2. Применить вычисленное смещение к конкретному экземпляру Profile.196pr = profile.Profile()197pr.bias = your_computed_bias198199# 3. Указать вычисленное смещение в конструкторе экземпляра.200pr = profile.Profile(bias=your_computed_bias)201```202203Если есть выбор, лучше выбрать меньшую константу – тогда результаты будут «реже» отображаться как отрицательные в статистике профилирования.204205## Использование пользовательского таймера206207Если требуется изменить способ определения текущего времени (например, принудительно использовать wall-clock time или время процесса), передайте нужную функцию таймера в конструктор класса [`Profile`](https://python-all.ru/3.16/library/profile.html#profile.Profile):208209```python210pr = profile.Profile(your_time_func)211```212213Полученный профилировщик будет вызывать `your_time_func`. В зависимости от того, используется ли [`profile.Profile`](https://python-all.ru/3.16/library/profile.html#profile.Profile) или [`profiling.tracing.Profile`](https://python-all.ru/3.16/library/profiling.tracing.html#profiling.tracing.Profile), возвращаемое значение `your_time_func` будет интерпретироваться по-разному:214215**[`profile.Profile`](https://python-all.ru/3.16/library/profile.html#profile.Profile)**216217`your_time_func` должна возвращать одно число или список чисел, сумма которых равна текущему времени (как [`os.times()`](https://python-all.ru/3.16/library/os.html#os.times)). Если функция возвращает одно число времени или длина возвращаемого списка равна 2, то будет использована особенно быстрая версия диспетчерской подпрограммы.218219Следует помнить, что необходимо откалибровать класс профилировщика для выбранной функции таймера (см. [Калибровка](https://python-all.ru/3.16/library/profile.html#profile-calibration)). Для большинства машин таймер, возвращающий одно целое число, даёт наилучшие результаты с точки зрения низких накладных расходов при профилировании. ([`os.times()`](https://python-all.ru/3.16/library/os.html#os.times) *довольно* плох, так как возвращает кортеж чисел с плавающей запятой). Если требуется заменить таймер на более качественный наиболее чистым способом, создайте производный класс и жёстко задайте замещающий метод диспетчеризации, который наилучшим образом обрабатывает вызов таймера, вместе с соответствующим калибровочным коэффициентом.220221**[`profiling.tracing.Profile`](https://python-all.ru/3.16/library/profiling.tracing.html#profiling.tracing.Profile)**222223`your_time_func` должна возвращать одно число. Если она возвращает целые числа, можно также вызвать конструктор класса со вторым аргументом, указывающим реальную длительность одной единицы времени. Например, если `your_integer_time_func` возвращает время, измеряемое в тысячах секунд, экземпляр [`Profile`](https://python-all.ru/3.16/library/profile.html#profile.Profile) конструируется следующим образом:224225```python226pr = profiling.tracing.Profile(your_integer_time_func, 0.001)227```228229Поскольку класс [`profiling.tracing.Profile`](https://python-all.ru/3.16/library/profiling.tracing.html#profiling.tracing.Profile) не может быть откалиброван, пользовательские функции таймера следует использовать с осторожностью и делать их как можно более быстрыми. Для достижения наилучших результатов с пользовательским таймером может потребоваться жёстко прописать его в коде C внутреннего модуля `_lsprof`.230231Python 3.3 добавляет несколько новых функций в [`time`](https://python-all.ru/3.16/library/time.html#module-time), которые можно использовать для точных измерений процессорного или реального времени. Например, см. [`time.perf_counter()`](https://python-all.ru/3.16/library/time.html#time.perf_counter).232233> **См. также**234>235> **[`profiling`](https://python-all.ru/3.16/library/profiling.html#module-profiling)**236>237> Обзор инструментов профилирования Python.238>239> **[`profiling.tracing`](https://python-all.ru/3.16/library/profiling.tracing.html#module-profiling.tracing)**240>241> Рекомендуемая замена для этого модуля.242>243> **[`pstats`](https://python-all.ru/3.16/library/pstats.html#module-pstats)**244>245> Статистический анализ и форматирование данных профиля.246