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

profiling.sampling.md

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

1> **Источник:** https://python-all.ru/3.15/library/profiling.sampling.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `profiling.sampling` – Статистический профилировщик89Добавлено в версии 3.15.1011**Исходный код:** [Lib/profiling/sampling/](https://python-all.ru/src/3.15/Lib/profiling/sampling)1213---1415[![Логотип Tachyon](https://python-all.ru/3.15/_images/tachyon-logo.png)](https://python-all.ru/3.15/_images/tachyon-logo.png)1617Модуль `profiling.sampling`, называемый **Tachyon**, предоставляет статистическое профилирование программ Python с помощью периодической выборки стека. Tachyon может запускать скрипты напрямую или подключаться к любому работающему процессу Python без необходимости изменять код или перезапускать. Поскольку выборка происходит извне целевого процесса, накладные расходы практически нулевые, что делает Tachyon подходящим как для разработки, так и для рабочей среды.1819## Что такое статистическое профилирование?2021Статистическое профилирование строит картину поведения программы, периодически захватывая снимки стека вызовов. Вместо того чтобы инструментировать каждый вызов и возврат функции, как это делают детерминированные профилировщики, Tachyon считывает стек вызовов через равные промежутки времени, чтобы записать, какой код выполняется в данный момент.2223Этот подход основан на простом принципе: функции, потребляющие значительное время процессора, будут часто появляться в собранных образцах. Собирая тысячи образцов за сеанс профилирования, Tachyon строит точную статистическую оценку того, где тратится время. Чем больше образцов собрано, тем точнее становится эта оценка.2425Следующая интерактивная визуализация демонстрирует, как работает профилирование с выборкой. Нажмите **Play**, чтобы наблюдать выполнение программы Python, и посмотрите, как профилировщик периодически захватывает снимки стека вызовов. Измените **интервал выборки**, чтобы увидеть, как частота выборки влияет на результаты.2627### Как оценивается время2829Значения времени, отображаемые в выводе Tachyon, – это **оценки, полученные из количества образцов**, а не прямые измерения. Tachyon подсчитывает, сколько раз каждая функция появляется в собранных образцах, а затем умножает на интервал выборки для оценки времени.3031Например, при частоте выборки 10 кГц в течение 10-секундного профиля Tachyon собирает приблизительно 100 000 образцов. Если функция появляется в 5 000 образцов (5% от общего числа), Tachyon оценивает, что она потребляла 5% от 10-секундной длительности, или около 500 миллисекунд. Это статистическая оценка, а не точное измерение.3233Точность этих оценок зависит от количества образцов. При 100 000 образцов функция, показывающая 5%, имеет погрешность примерно ±0,5%. При всего 1 000 образцов то же измерение 5% может фактически представлять от 3% до 7% реального времени.3435Вот почему большая длительность профилирования и более короткие интервалы выборки дают более надежные результаты – они собирают больше образцов. Для большинства задач анализа производительности настройки по умолчанию обеспечивают достаточную точность для выявления узких мест и определения направлений оптимизации.3637Поскольку выборка является статистической, результаты будут незначительно различаться между запусками. Функция, показывающая 12% в одном запуске, может показать 11% или 13% в следующем. Это нормально и ожидаемо. Сосредоточьтесь на общей картине, а не на точных процентах, и не беспокойтесь о небольших расхождениях между запусками.3839### Когда использовать другой подход4041Статистическая выборка подходит не для любой ситуации.4243Для очень коротких скриптов, которые завершаются менее чем за секунду, профилировщик может не собрать достаточно образцов для надежных результатов. Вместо этого используйте [`profiling.tracing`](https://python-all.ru/3.15/library/profiling.tracing.html#module-profiling.tracing) или запустите скрипт в цикле, чтобы увеличить время профилирования.4445Когда вам нужно точное количество вызовов, выборка не может его предоставить. Выборка оценивает частоту по снимкам, поэтому, если вам нужно точно знать, сколько раз была вызвана функция, используйте [`profiling.tracing`](https://python-all.ru/3.15/library/profiling.tracing.html#module-profiling.tracing).4647При сравнении двух реализаций, где разница может составлять всего 1-2%, шум выборки может скрыть реальные различия. Используйте [`timeit`](https://python-all.ru/3.15/library/timeit.html#module-timeit) для микробенчмарков или [`profiling.tracing`](https://python-all.ru/3.15/library/profiling.tracing.html#module-profiling.tracing) для точных измерений.4849Ключевое отличие от [`profiling.tracing`](https://python-all.ru/3.15/library/profiling.tracing.html#module-profiling.tracing) заключается в том, как происходит измерение. Трассировочный профилировщик инструментирует ваш код, записывая каждый вызов функции и возврат. Это обеспечивает точное количество вызовов и точное время, но добавляет накладные расходы на каждый вызов функции. Профилировщик с выборкой, напротив, наблюдает за программой извне через фиксированные интервалы, не изменяя её выполнение. Представьте разницу так: трассировка – это как если бы кто-то следовал за вами и записывал каждый ваш шаг, а выборка – это как делать фотографии каждую секунду и восстанавливать ваш путь по этим снимкам.5051Эта модель внешнего наблюдения делает профилирование с выборкой практичным для использования в рабочей среде. Профилируемая программа работает на полной скорости, потому что внутри неё не выполняется код инструментирования, и целевой процесс никогда не останавливается и не приостанавливается во время выборки – Tachyon читает стек вызовов непосредственно из памяти процесса, пока он продолжает выполняться. Вы можете подключиться к работающему серверу, собрать данные и отключиться, не давая приложению узнать, что за ним наблюдали. Оборотная сторона в том, что очень короткие функции могут быть пропущены, если они успевают завершиться между выборками.5253Статистическое профилирование отлично подходит для ответа на вопрос: «Где моя программа тратит время?» Оно выявляет горячие точки и узкие места в рабочем коде, где накладные расходы детерминированного профилирования были бы неприемлемы. Для точного количества вызовов и полных графов вызовов используйте [`profiling.tracing`](https://python-all.ru/3.15/library/profiling.tracing.html#module-profiling.tracing).5455## Быстрые примеры5657Профилировать скрипт и сразу увидеть результаты:5859```sh60python -m profiling.sampling run script.py61```6263Профилировать модуль с аргументами:6465```sh66python -m profiling.sampling run -m mypackage.module arg1 arg267```6869Создать интерактивный флейм-граф:7071```sh72python -m profiling.sampling run --flamegraph -o profile.html script.py73```7475Подключиться к работающему процессу по PID:7677```sh78python -m profiling.sampling attach 1234579```8081Вывести один снимок стека работающего процесса:8283```sh84python -m profiling.sampling dump 1234585```8687Использовать режим реального времени для мониторинга (нажмите `q` для выхода):8889```sh90python -m profiling.sampling run --live script.py91```9293Профилировать в течение 60 секунд с более высокой частотой выборки:9495```sh96python -m profiling.sampling run -d 60 -r 20khz script.py97```9899Создать построчную тепловую карту:100101```sh102python -m profiling.sampling run --heatmap script.py103```104105Включение профилирования на уровне опкодов, чтобы видеть, какие инструкции байт-кода выполняются:106107```sh108python -m profiling.sampling run --opcodes --flamegraph script.py109```110111## Команды112113Tachyon работает через несколько подкоманд. `run` и `attach` собирают семплы с течением времени; `dump` делает единичный снимок; `replay` преобразует бинарные профили в другие форматы.114115### Команда `run`116117Команда `run` запускает Python-скрипт или модуль и профилирует его с момента запуска:118119```sh120python -m profiling.sampling run script.py121python -m profiling.sampling run -m mypackage.module122```123124При профилировании скрипта профилировщик запускает цель в подпроцессе, ждёт её инициализации, а затем начинает сбор семплов. Флаг `-m` указывает, что цель должна быть запущена как модуль (эквивалентно `python -m`). Аргументы после цели передаются профилируемой программе:125126```sh127python -m profiling.sampling run script.py --config settings.yaml128```129130### Команда `attach`131132Команда `attach` подключается к уже запущенному процессу Python по его идентификатору процесса:133134```sh135python -m profiling.sampling attach 12345136```137138Эта команда особенно полезна для исследования проблем производительности в рабочих системах. Целевой процесс не требует модификации и не требует перезапуска. Профилировщик подключается, собирает семплы в течение заданного времени, затем отключается и формирует результат.139140```sh141python -m profiling.sampling attach --live 12345142python -m profiling.sampling attach --flamegraph -d 30 -o profile.html 12345143```144145На большинстве систем для подключения к другому процессу требуются соответствующие разрешения. См. [Требования к платформе](https://python-all.ru/3.15/library/profiling.sampling.html#profiling-permissions) для уточнения требований для конкретной платформы.146147### Команда `dump`148149Команда `dump` выводит единичный снимок стека Python работающего процесса и завершается, аналогично traceback:150151```sh152python -m profiling.sampling dump 12345153```154155В отличие от `attach`, `dump` не выполняет цикл семплирования: он читает стек один раз. Это полезно для изучения зависших или неотвечающих процессов, а также для ответа на вопрос «что сейчас делает этот процесс?».156157Вывод повторяет формат traceback (самый последний вызов вверху) и помечает каждый поток его текущим состоянием (главный поток, владеет GIL, на процессоре, ожидает GIL, есть исключение или бездействует):158159```text160Stack dump for PID 12345, thread 140735 (main thread, has GIL, on CPU; most recent call last):161  File "server.py", line 28, in serve162    await handle_request(req)163  File "handler.py", line 91, in handle_request164    result = expensive_call(req)165```166167Когда исходные файлы цели доступны для чтения, `dump` выводит строку исходного кода для каждого фрейма и подсвечивает выполняемое выражение.168169Как и `attach`, `dump` требует разрешения на чтение памяти целевого процесса. См. [Требования к платформе](https://python-all.ru/3.15/library/profiling.sampling.html#profiling-permissions).170171Команда `dump` поддерживает следующие параметры:172173**`-a`, `--all-threads`**174175Выгрузить каждый поток в целевом процессе. Без этого флага показывается только главный поток.176177**`--native`**178179Включать синтетические фреймы `<native>`, отмечающие переходы в C-расширения или другой не-Python код.180181**`--no-gc`**182183Скрывать синтетические фреймы `<GC>`, которые отмечают активную сборку мусора.184185**`--opcodes`**186187Аннотировать каждый фрейм опкодом байт-кода, который в данный момент выполняет поток (например, `opcode=CALL_KW`). Полезно для исследования на уровне инструкций, включая определение специализаций, выбранных адаптивным интерпретатором.188189**`--async-aware`**190191Восстанавливать стеки через границы `await`. `dump` обходит граф задач и выводит по одному разделу на задачу, с маркерами `<task>`, разделяющими корутины, ожидающие друг друга.192193**`--async-mode {running,all}`**194195Управляет тем, какие задачи включаются, когда `--async-aware` включён. `running` показывает только задачу, выполняющуюся в данный момент в каждом потоке; `all` (по умолчанию для `dump`) также включает задачи, приостановленные в ожидании. У `attach` значение по умолчанию для этого флага – `running`; `dump` по умолчанию `all`, потому что один снимок наиболее полезен, когда показывает полный граф задач.196197**`--blocking`**198199Приостанавливать каждый поток в цели во время чтения его стека и возобновлять их после. Гарантирует полностью согласованный снимок ценой кратковременной остановки цели. Без этого `dump` читает память, пока цель продолжает работу, что быстрее, но иногда может привести к разорванному стеку.200201### Команда `replay`202203Команда `replay` преобразует бинарные файлы профилей в другие форматы вывода:204205```sh206python -m profiling.sampling replay profile.bin207python -m profiling.sampling replay --flamegraph -o profile.html profile.bin208```209210Эта команда полезна, когда собранные данные профилирования в бинарном формате требуется проанализировать позже или преобразовать в формат для визуализации. Бинарные профили можно многократно преобразовывать в разные форматы без повторного профилирования.211212```sh213# Преобразовать двоичный файл в pstats (по умолчанию, вывод в stdout)214python -m profiling.sampling replay profile.bin215216# Преобразовать двоичный файл в граф пламени217python -m profiling.sampling replay --flamegraph -o output.html profile.bin218219# Преобразовать двоичный файл в формат gecko для Firefox Profiler220python -m profiling.sampling replay --gecko -o profile.json profile.bin221222# Преобразовать двоичный файл в тепловую карту223python -m profiling.sampling replay --heatmap -o my_heatmap profile.bin224```225226### Профилирование в рабочей среде227228Семплирующий профилировщик предназначен для использования в рабочей среде. Он не создаёт измеримой нагрузки на целевой процесс, поскольку читает память извне, а не инструментирует код. Целевое приложение продолжает работать на полной скорости и не знает, что его профилируют.229230При профилировании рабочих систем следует помнить следующие рекомендации:231232Начинайте с коротких интервалов (10–30 секунд), чтобы получить быстрые результаты, а затем увеличивайте, если требуется большая статистическая точность. По умолчанию профилирование выполняется до завершения целевого процесса, чего обычно достаточно для выявления основных «горячих точек».233234По возможности профилируйте при типичной нагрузке, а не при пиковом трафике. Профили, собранные при нормальной работе, легче интерпретировать, чем собранные во время необычных всплесков.235236Профилировщик сам потребляет ресурсы CPU на машине, где он запущен (не на целевом процессе). На той же машине это обычно незначительно. При профилировании удалённых процессов сетевая задержка не влияет на целевой процесс.237238Результаты на продакшне могут отличаться от результатов на разработке из-за разного размера данных, конкурентной нагрузки или эффектов кэширования. Это ожидаемо и зачастую именно то, что нужно зафиксировать.239240### Требования к платформе241242Профилировщик читает память целевого процесса для захвата стеков вызовов. На большинстве операционных систем для этого требуются повышенные привилегии.243244**Linux**245246В Linux профилировщик использует `ptrace` или `process_vm_readv` для чтения памяти целевого процесса. Обычно для этого требуется одно из:247248- Запуск от root249- Наличие возможности `CAP_SYS_PTRACE`250- Настройка области ptrace Yama: `/proc/sys/kernel/yama/ptrace_scope`251252Значение ptrace\_scope по умолчанию (1) ограничивает ptrace только родительскими процессами. Чтобы разрешить прикрепление к любому процессу того же пользователя, установите его в 0:253254```sh255echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope256```257258**macOS**259260В macOS профилировщик использует `task_for_pid()` для доступа к целевому процессу. Для этого требуется одно из:261262- Запуск от root263- Наличие у бинарного файла профилировщика права `com.apple.security.cs.debugger`264- Отключение System Integrity Protection (SIP) (не рекомендуется)265266**Windows**267268В Windows профилировщику требуются права администратора или привилегия `SeDebugPrivilege` для чтения памяти другого процесса.269270### Совместимость версий271272Профилировщик и целевой процесс должны работать с одной и той же минорной версией Python (например, обе Python 3.15). Прикрепление из Python 3.14 к процессу Python 3.15 не поддерживается.273274Дополнительные ограничения действуют для предварительных версий Python: если профилировщик или цель запущены на предварительной версии (альфа, бета или кандидат в релиз), обе должны использовать точно ту же версию.275276В сборках Python со свободной многопоточностью (free-threaded) профилировщик не может прикрепиться из сборки с свободной многопоточностью к стандартной сборке и наоборот.277278## Конфигурация сэмплирования279280Прежде чем рассматривать различные форматы вывода и варианты визуализации, важно понять, как настроить сам процесс сэмплирования. Профилировщик предоставляет несколько опций, управляющих частотой сбора образцов, продолжительностью профилирования, отслеживаемыми потоками и дополнительным контекстом, захватываемым в каждом образце.281282Конфигурация по умолчанию хорошо подходит для большинства случаев использования:283284| Параметр | По умолчанию |285| --- | --- |286| Значение по умолчанию для `--sampling-rate` / `-r` | 1 кГц |287| Значение по умолчанию для `--duration` / `-d` | Выполнение до завершения |288| Значение по умолчанию для `--all-threads` / `-a` | Только главный поток |289| По умолчанию для `--native` | Нет фреймов `<native>` (время C-кода относится к вызывающему) |290| По умолчанию для `--no-gc` | Фреймы `<GC>` включаются, когда активна сборка мусора |291| По умолчанию для `--mode` | Режим реального времени (все семплы записываются) |292| По умолчанию для `--realtime-stats` | Отключено |293| По умолчанию для `--subprocesses` | Отключено |294| По умолчанию для `--blocking` | Отключено (неблокирующий сбор семплов) |295296### Частота и длительность семплирования297298Два самых основных параметра – это частота и длительность семплирования. Вместе они определяют, сколько семплов будет собрано за сеанс профилирования.299300Параметр [`--sampling-rate`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-r) ([`-r`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-r)) задаёт, как часто собираются семплы. По умолчанию – 1 кГц (1000 семплов в секунду):301302```sh303python -m profiling.sampling run -r 20khz script.py304```305306Более высокая частота даёт больше семплов и более детальные данные ценой небольшого увеличения нагрузки на процессор профилировщика. Более низкая частота снижает накладные расходы профилировщика, но может пропустить короткоживущие функции. Для большинства приложений частота по умолчанию обеспечивает хороший баланс между точностью и накладными расходами.307308Параметр [`--duration`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-d) ([`-d`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-d)) задаёт длительность профилирования в секундах. По умолчанию профилирование продолжается, пока целевой процесс не завершится или не будет прерван:309310```sh311python -m profiling.sampling run -d 60 script.py312```313314Указание длительности полезно при подключении к долго работающим процессам или когда нужно ограничить профилирование определённым временным окном. При профилировании скрипта обычно достаточно поведения по умолчанию – работы до завершения.315316### Выбор потоков317318Программы на Python часто используют несколько потоков – явно через модуль [`threading`](https://python-all.ru/3.15/library/threading.html#module-threading) или неявно через библиотеки, управляющие пулами потоков.319320По умолчанию профилировщик собирает семплы только главного потока. Параметр [`--all-threads`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-a) ([`-a`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-a)) включает сбор семплов всех потоков в процессе:321322```sh323python -m profiling.sampling run -a script.py324```325326Многопоточное профилирование показывает, как работа распределяется между потоками, и позволяет выявить заблокированные или голодающие потоки. Семплы каждого потока объединяются в выводе, с возможностью фильтрации по потоку в некоторых форматах. Эта возможность особенно полезна при исследовании проблем параллелизма или когда работа распределяется по пулу потоков.327328### Блокирующий режим329330По умолчанию Tachyon читает память целевого процесса, не останавливая его. Такой неблокирующий подход идеален для большинства сценариев профилирования, поскольку практически не создаёт нагрузки на целевое приложение: профилируемая программа работает на полной скорости и не подозревает, что за ней наблюдают.331332Однако неблокирующий сбор семплов иногда может давать неполные или некорректные стек-трейсы в приложениях с большим количеством генераторов или корутин, которые быстро переключаются между точками yield, или в программах с очень быстро меняющимися стеками вызовов, где функции входят и выходят между началом и концом одного чтения стека, что приводит к восстановленным стекам, смешивающим фреймы из разных состояний выполнения или фреймы, которых на самом деле никогда не существовало.333334Для таких случаев параметр [`--blocking`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-blocking) останавливает целевой процесс во время каждого семпла:335336```sh337python -m profiling.sampling run --blocking script.py338python -m profiling.sampling attach --blocking 12345339```340341Когда блокирующий режим включён, профилировщик приостанавливает целевой процесс, читает его стек, а затем возобновляет. Это гарантирует, что каждый захваченный стек представляет собой реальный, согласованный снимок того, что процесс делал в этот момент. Оборотная сторона – целевой процесс работает медленнее, поскольку его многократно приостанавливают.342343> **Предупреждение**344>345> Не используйте очень высокие частоты семплирования (маленькие значения `--interval`) с блокирующим режимом. Приостановка и возобновление процесса занимает время, и если интервал семплирования слишком короткий, целевой процесс будет проводить больше времени в остановленном состоянии, чем в работающем. Для блокирующего режима рекомендуются интервалы от 1000 микросекунд (1 миллисекунда) и выше. Интервал по умолчанию в 100 микросекунд может вызвать заметное замедление целевого приложения.346347Используйте блокирующий режим только при обнаружении некорректных стеков в профилях, особенно в коде с большим количеством генераторов или корутин. Для большинства приложений неблокирующий режим по умолчанию даёт точные результаты без влияния на целевой процесс.348349### Специальные фреймы350351Профилировщик может внедрять искусственные фреймы в захваченные стеки, чтобы дать дополнительный контекст о том, что делает интерпретатор в момент взятия каждого семпла. Эти синтетические фреймы помогают различать разные типы выполнения, которые иначе были бы невидимы.352353Параметр [`--native`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-native) добавляет фреймы `<native>`, указывающие, когда Python вызвал C-код (модули расширений, встроенные функции или сам интерпретатор):354355```sh356python -m profiling.sampling run --native script.py357```358359Эти фреймы помогают отличить время, затраченное в коде Python, от времени, затраченного в нативных библиотеках. Без этой опции выполнение нативного кода выглядит как время в функции Python, которая сделала вызов. Это полезно при оптимизации кода, активно использующего C-расширения, такие как NumPy или драйверы баз данных.360361По умолчанию профилировщик включает фреймы `<GC>`, когда активна сборка мусора. Параметр [`--no-gc`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-no-gc) подавляет эти фреймы:362363```sh364python -m profiling.sampling run --no-gc script.py365```366367Фреймы GC помогают выявить программы, где сборка мусора отнимает значительное время, что может указывать на шаблоны распределения памяти, которые стоит оптимизировать. Если вы видите значительное время во фреймах `<GC>`, стоит изучить частоту создания объектов или использовать пул объектов.368369### Профилирование с учётом опкодов370371Опция [`--opcodes`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-opcodes) включает профилирование на уровне инструкций, которое фиксирует, какие инструкции байт-кода Python выполняются на каждом сэмпле:372373```sh374python -m profiling.sampling run --opcodes --flamegraph script.py375```376377Эта возможность обеспечивает видимость выполнения байт-кода Python, включая оптимизации адаптивной специализации. Когда общая инструкция вроде `LOAD_ATTR` во время выполнения специализируется в более эффективный вариант вроде `LOAD_ATTR_INSTANCE_VALUE`, профилировщик показывает как специализированное имя, так и базовую инструкцию.378379Информация об опкодах отображается в нескольких форматах вывода:380381- **Графики пламени**: при наведении на кадр отображается всплывающая подсказка с разбивкой инструкций байт-кода, показывающая, какие опкоды заняли время в этой функции.382- **Тепловая карта**: раскрываемые панели байт-кода для каждой строки исходного кода показывают разбивку инструкций с процентами специализации.383- **Режим реального времени**: панель опкодов показывает статистику на уровне инструкций для выбранной функции, доступную через навигацию с клавиатуры.384- **Формат Gecko**: переходы опкодов выводятся в виде маркеров интервалов на временной шкале профилировщика Firefox.385386Такой уровень детализации особенно полезен для:387388- понимания влияния на производительность адаптивной специализации Python389- выявления горячих инструкций байт-кода, которые могут выиграть от оптимизации390- анализа эффективности различных шаблонов кода на уровне инструкций391- отладки проблем производительности, возникающих на уровне байт-кода392393Опция [`--opcodes`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-opcodes) совместима с форматами [`--live`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-live), [`--flamegraph`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-flamegraph), [`--heatmap`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-heatmap) и [`--gecko`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-gecko). Она требует дополнительной памяти для хранения информации об опкодах и может немного снизить производительность сэмплирования, но обеспечивает беспрецедентную видимость модели выполнения Python.394395### Статистика в реальном времени396397Опция [`--realtime-stats`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-realtime-stats) отображает статистику частоты сэмплирования во время профилирования:398399```sh400python -m profiling.sampling run --realtime-stats script.py401```402403Это показывает фактическую достигнутую частоту сэмплирования, которая может быть ниже запрошенной, если профилировщик не успевает. Статистика помогает убедиться, что профилирование работает корректно и собирается достаточное количество сэмплов. См. [Эффективность сэмплирования](https://python-all.ru/3.15/library/profiling.sampling.html#sampling-efficiency) для подробностей об интерпретации этих метрик.404405### Профилирование подпроцессов406407Опция [`--subprocesses`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-subprocesses) включает автоматическое профилирование подпроцессов, порождённых целью:408409```sh410python -m profiling.sampling run --subprocesses script.py411python -m profiling.sampling attach --subprocesses 12345412```413414При включении профилировщик отслеживает создание дочерних процессов у целевого процесса. Когда обнаружен новый дочерний процесс Python, автоматически запускается отдельный экземпляр профилировщика для его профилирования. Это полезно для приложений, использующих [`multiprocessing`](https://python-all.ru/3.15/library/multiprocessing.html#module-multiprocessing), [`subprocess`](https://python-all.ru/3.15/library/subprocess.html#module-subprocess), [`concurrent.futures`](https://python-all.ru/3.15/library/concurrent.futures.html#module-concurrent.futures) с [`ProcessPoolExecutor`](https://python-all.ru/3.15/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor) или другие механизмы порождения процессов.415416worker\_pool.py417418```python419from concurrent.futures import ProcessPoolExecutor420import math421422def compute_factorial(n):423    total = 0424    for i in range(50):425        total += math.factorial(n)426    return total427428if __name__ == "__main__":429    numbers = [5000 + i * 100 for i in range(50)]430    with ProcessPoolExecutor(max_workers=4) as executor:431        results = list(executor.map(compute_factorial, numbers))432    print(f"Computed {len(results)} factorials")433```434435```sh436python -m profiling.sampling run --subprocesses --flamegraph worker_pool.py437```438439Это создаёт отдельные графики пламени для основного процесса и каждого рабочего процесса: `flamegraph_<main_pid>.html`, `flamegraph_<worker1_pid>.html` и так далее.440441Каждый подпроцесс получает свой собственный выходной файл. Имя файла образуется из указанного пути вывода (или пути по умолчанию) с добавлением идентификатора процесса подпроцесса:442443- Если указать `-o profile.html`, подпроцессы создадут `profile_12345.html`, `profile_12346.html` и так далее.444- При выводе по умолчанию подпроцессы создают файлы вроде `flamegraph_12345.html` или каталоги вроде `heatmap_12345`.445- Для формата pstats (по умолчанию выводящего в stdout) подпроцессы создают файлы вроде `profile_12345.pstats`.446447Профилировщики подпроцессов наследуют большинство параметров сэмплирования от родительского (частоту сэмплирования, длительность, выбор потоков, нативные кадры, кадры GC, режим учёта асинхронности и формат вывода). Все дочерние процессы Python профилируются рекурсивно, включая внуков и дальнейших потомков.448449Обнаружение подпроцессов работает путём периодического сканирования новых потомков целевого процесса и проверки, является ли каждый новый процесс процессом Python, путём зондирования памяти процесса на наличие структур времени выполнения Python. Непроцессы Python (например, команды оболочки или внешние инструменты) игнорируются.450451Существует ограничение в 100 одновременных профилировщиков подпроцессов, чтобы предотвратить истощение ресурсов в программах, порождающих много процессов. Если этот лимит достигнут, дополнительные подпроцессы не профилируются, и выводится предупреждение.452453Опция [`--subprocesses`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-subprocesses) несовместима с режимом [`--live`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-live), поскольку режим реального времени использует интерактивный терминальный интерфейс, который не может поддерживать несколько одновременных отображений профилировщика.454455### Эффективность сэмплирования456457Метрики эффективности сэмплирования помогают оценить качество собранных данных. Эти метрики отображаются в терминальном выводе профилировщика и на боковой панели графика пламени.458459**Эффективность сэмплирования** – это процент успешных попыток взятия сэмпла. Каждая попытка сэмплирования читает стек вызовов целевого процесса из памяти. Попытка может завершиться неудачей, если процесс находится в несогласованном состоянии в момент чтения, например, во время переключения контекста или пока интерпретатор обновляет свои внутренние структуры. Низкая эффективность может указывать на то, что профилировщик не успевает за запрошенной частотой сэмплирования, часто из-за загрузки системы или слишком агрессивной настройки интервала.460461**Пропущенные сэмплы** – это процент ожидаемых сэмплов, которые не были собраны. На основе заданного интервала и длительности профилировщик ожидает собрать определённое количество сэмплов. Некоторые сэмплы могут быть пропущены, если профилировщик отстаёт от графика, например, при высокой нагрузке на систему. Небольшой процент пропущенных сэмплов нормален и незначительно влияет на статистическую точность профиля.462463Обе метрики носят информационный характер. Даже при наличии неудачных попыток или пропущенных сэмплов профиль остаётся статистически валидным, пока собрано достаточно сэмплов. Профилировщик сообщает фактическое количество захваченных сэмплов, которое можно использовать, чтобы оценить, достаточно ли данных для анализа.464465## Режимы профилирования466467Сэмплирующий профилировщик поддерживает четыре режима, которые определяют, какие сэмплы записываются. Режим задаёт, что измеряет профиль: общее затраченное время, время выполнения на CPU, время удержания глобальной блокировки интерпретатора или обработку исключений.468469### Режим календарного времени470471Режим календарного времени ([`--mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-mode)`=wall`) захватывает все сэмплы независимо от того, чем занят поток. Это режим по умолчанию, который даёт полную картину того, где тратится время при выполнении программы:472473```sh474python -m profiling.sampling run --mode=wall script.py475```476477В режиме календарного времени сэмплы записываются независимо от того, активно ли поток выполняет код Python, ожидает ввода-вывода, заблокирован на блокировке или спит. Это делает профилирование в режиме календарного времени идеальным для понимания общего распределения времени в программе, включая время ожидания.478479Если программа тратит значительное время на операции ввода-вывода, сетевые вызовы или сон, режим календарного времени покажет это ожидание как время, приписанное вызывающей функции. Часто это именно то, что нужно при оптимизации сквозной задержки.480481### Режим CPU482483Режим CPU ([`--mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-mode)`=cpu`) записывает сэмплы только тогда, когда поток фактически выполняется на ядре CPU:484485```sh486python -m profiling.sampling run --mode=cpu script.py487```488489Сэмплы, взятые во время сна потока, блокировки на I/O или ожидания блокировки, отбрасываются. Полученный профиль показывает, где расходуются циклы CPU, отфильтровывая время простоя.490491Режим CPU полезен, когда нужно сосредоточиться на вычислительных горячих точках, не отвлекаясь на ожидания I/O. Если программа чередует вычисления и сетевые вызовы, режим CPU показывает, какие вычислительные участки наиболее затратны.492493### Сравнение профилей календарного времени и CPU494495Запуск профилей в режиме календарного времени и CPU может показать, тратит ли функция время на вычисления или ожидание.496497Если функция заметно проявляется в обоих профилях, это истинная вычислительная горячая точка – активно использует CPU. Оптимизация должна быть направлена на улучшение алгоритмов или более эффективный код.498499Если функция высока в режиме календарного времени, но низка или отсутствует в режиме CPU, она привязана к I/O или ожиданию. Функция тратит большую часть времени на ожидание сети, диска, блокировок или сна. Оптимизация CPU здесь не поможет; вместо этого рассмотрите асинхронный I/O, пул соединений или сокращение времени ожидания.500501```python502import time503504def do_sleep():505    time.sleep(2)506507def do_compute():508    sum(i**2 for i in range(1000000))509510if __name__ == "__main__":511    do_sleep()512    do_compute()513```514515```sh516python -m profiling.sampling run --mode=wall script.py  # do_sleep ~98%, do_compute ~1%517python -m profiling.sampling run --mode=cpu script.py   # do_sleep отсутствует, преобладает do_compute518```519520### Режим GIL521522Режим GIL ([`--mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-mode)`=gil`) записывает сэмплы только тогда, когда поток удерживает глобальную блокировку интерпретатора Python:523524```sh525python -m profiling.sampling run --mode=gil script.py526```527528GIL удерживается только во время выполнения байт-кода Python. Когда Python вызывает расширения C, выполняет операции ввода-вывода или исполняет нативный код, GIL обычно освобождается. Это означает, что режим GIL фактически измеряет время, затраченное на выполнение именно кода Python, отфильтровывая время в нативных библиотеках.529530В многопоточных программах режим GIL показывает, какой код мешает другим потокам выполнять байт-код Python. Поскольку только один поток может одновременно удерживать GIL, функции, которые часто появляются в профилях режима GIL, монополизируют интерпретатор.531532Режим GIL помогает ответить на вопросы вроде «какие функции монополизируют GIL?» и «почему мои другие потоки голодают?». Он также может быть полезен в однопоточных программах, чтобы отличить время выполнения Python от времени, затраченного в расширениях C или I/O.533534```python535import hashlib536537def hash_work():538    # Расширение на C – освобождает GIL во время вычислений539    for _ in range(200):540        hashlib.sha256(b"data" * 250000).hexdigest()541542def python_work():543    # Чистый Python – удерживает GIL во время вычислений544    for _ in range(3):545        sum(i**2 for i in range(1000000))546547if __name__ == "__main__":548    hash_work()549    python_work()550```551552```sh553python -m profiling.sampling run --mode=cpu script.py  # hash_work ~42%, python_work ~38%554python -m profiling.sampling run --mode=gil script.py  # hash_work ~5%, python_work ~60%555```556557### Режим исключений558559Режим исключений (`--mode=exception`) записывает сэмплы только тогда, когда у потока активное исключение:560561```sh562python -m profiling.sampling run --mode=exception script.py563```564565Сэмплы записываются в двух ситуациях: когда исключение распространяется вверх по стеку вызовов (после `raise`, но до перехвата), или когда код выполняется внутри блока `except`, где информация об исключении всё ещё присутствует в состоянии потока.566567Следующий пример иллюстрирует, какие области кода захватываются:568569```python570def example():571    try:572        raise ValueError("error")    # Захвачено: возбуждается исключение573    except ValueError:574        process_error()              # Захвачено: внутри блока except575    finally:576        cleanup()                    # НЕ захвачено: исключение уже обработано577578def example_propagating():579    try:580        try:581            raise ValueError("error")582        finally:583            cleanup()                # Захвачено: исключение распространяется через584    except ValueError:585        pass586587def example_no_exception():588    try:589        do_work()590    finally:591        cleanup()                    # НЕ захвачено: исключение не задействовано592```593594Обратите внимание, что блоки `finally` захватываются только тогда, когда через них активно распространяется исключение. Как только блок `except` заканчивает выполнение, Python очищает информацию об исключении перед запуском любого последующего блока `finally`. Аналогично, блоки `finally`, которые выполняются при нормальном ходе (когда исключение не было возбуждено), не захватываются, поскольку состояние исключения отсутствует.595596Этот режим полезен для понимания того, где программа тратит время на обработку ошибок. Обработка исключений может быть значительным источником накладных расходов в коде, который использует исключения для управления потоком (например, `StopIteration` в итераторах) или в приложениях, обрабатывающих множество ошибочных ситуаций (например, сетевые серверы, обрабатывающие сбои соединений).597598Режим исключений помогает ответить на вопросы вроде «сколько времени тратится на обработку исключений?» и «какие обработчики исключений самые дорогие?». Он может выявить скрытые затраты производительности в коде, который перехватывает и обрабатывает множество исключений, даже если эти исключения обрабатываются корректно. Например, если библиотека синтаксического анализа внутренне использует исключения для сигнализации об ошибках формата, этот режим зафиксирует время, затраченное в этих обработчиках, даже если вызывающий код никогда не видит исключений.599600## Форматы вывода601602Профилировщик выводит результат в нескольких форматах, каждый из которых подходит для разных рабочих процессов анализа. Формат выбирается с помощью флага командной строки, а вывод направляется в stdout, файл или каталог в зависимости от формата.603604### Формат pstats605606Формат pstats ([`--pstats`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-pstats)) создаёт текстовую таблицу, аналогичную той, что генерируют детерминированные профилировщики. Это формат вывода по умолчанию:607608```sh609python -m profiling.sampling run script.py610python -m profiling.sampling run --pstats script.py611```612613[![Терминальный вывод Tachyon pstats](https://python-all.ru/3.15/_images/tachyon-pstats.png)](https://python-all.ru/3.15/_images/tachyon-pstats.png)614615Формат pstats отображает результаты профилирования в таблице с цветовой кодировкой, показывающей горячие точки функций, количество сэмплов и временные оценки.616617Вывод по умолчанию отображается в stdout:618619```sh620Profile Stats (Mode: wall):621     nsamples  sample%    tottime (ms)  cumul%   cumtime (ms)  filename:lineno(function)622       234/892    11.7%       234.00     44.6%       892.00    server.py:145(handle_request)623       156/156     7.8%       156.00      7.8%       156.00    <built-in>:0(socket.recv)624        98/421     4.9%        98.00     21.1%       421.00    parser.py:67(parse_message)625```626627Столбцы показывают количество сэмплов и примерное время:628629- **nsamples**: Отображается как `direct/cumulative` (например, `10/50`). Прямые сэмплы – это когда функция находилась на вершине стека и активно выполнялась. Накопительные сэмплы – когда функция появлялась где-либо в стеке, включая ожидание вызванных ею функций. Если функция показывает `10/50`, она напрямую выполнялась в 10 сэмплах и всего была в стеке вызовов в 50 сэмплах.630- **sample%** и **cumul%**: Проценты от общего количества сэмплов для прямых и накопительных значений соответственно.631- **tottime** и **cumtime**: Примерное астрономическое время на основе количества сэмплов и длительности профилирования. Единицы времени выбираются автоматически в зависимости от величины: секунды для больших значений, миллисекунды для средних или микросекунды для малых.632633Вывод содержит легенду, объясняющую каждый столбец, и сводку интересных функций, которая выделяет:634635- **Горячие точки**: Функции с высоким отношением прямых сэмплов к накопительным (отношение близко к 1,0). Эти функции тратят большую часть времени на выполнение собственного кода, а не на ожидание вызываемых функций. Высокие отношения указывают, где на самом деле расходуется процессорное время.636- **Косвенные вызовы**: Функции с большой разницей между накопительными и прямыми сэмплами. Это функции-оркестраторы, которые делегируют работу другим функциям. Они часто появляются в стеке, но редко на вершине.637- **Увеличение вызовов**: Функции, где накопительные сэмплы значительно превышают прямые (высокий множитель накопительных к прямым). Это часто вложенные функции, появляющиеся глубоко во многих цепочках вызовов.638639Используйте [`--no-summary`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-no-summary), чтобы скрыть как легенду, так и сводку.640641Чтобы сохранить вывод pstats в двоичный файл вместо stdout:642643```sh644python -m profiling.sampling run -o profile.pstats script.py645```646647Формат pstats поддерживает несколько опций для управления отображением. Опция [`--sort`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-sort) определяет столбец, используемый для упорядочивания результатов:648649```sh650python -m profiling.sampling run --sort=tottime script.py651python -m profiling.sampling run --sort=cumtime script.py652python -m profiling.sampling run --sort=nsamples script.py653```654655Опция [`--limit`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-l) ограничивает вывод первыми N записями:656657```sh658python -m profiling.sampling run --limit=30 script.py659```660661Опция [`--no-summary`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-no-summary) скрывает заголовок сводки, который предшествует таблице статистики.662663### Формат свернутых стеков664665Формат свернутых стеков ([`--collapsed`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-collapsed)) создаёт одну строку для каждого уникального стека вызовов с указанием количества сэмплов этого стека:666667```sh668python -m profiling.sampling run --collapsed script.py669```670671Вывод выглядит так:672673```text674main;process_data;parse_json;decode_utf8 42675main;process_data;parse_json 156676main;handle_request;send_response 89677```678679Каждая строка содержит названия функций, разделённые точкой с запятой, представляющие стек вызовов снизу вверх, затем пробел и количество сэмплов. Этот формат предназначен для совместимости с внешними инструментами построения флейм-графов, в частности со скриптом `flamegraph.pl` Брендана Грегга.680681Чтобы построить флейм-граф на основе свернутых стеков:682683```sh684python -m profiling.sampling run --collapsed script.py > stacks.txt685flamegraph.pl stacks.txt > profile.svg686```687688Полученный SVG можно открыть в любом веб-браузере; он предоставляет интерактивную визуализацию, где можно щёлкать для увеличения отдельных путей вызовов.689690### Формат флейм-графа691692Формат флейм-графа ([`--flamegraph`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-flamegraph)) создаёт самодостаточный HTML-файл с интерактивной визуализацией флейм-графа:693694```sh695python -m profiling.sampling run --flamegraph script.py696python -m profiling.sampling run --flamegraph -o profile.html script.py697```698699[![Интерактивный флейм-граф Tachyon](https://python-all.ru/3.15/_images/tachyon-flamegraph.png)](https://python-all.ru/3.15/_images/tachyon-flamegraph.png)700701Визуализация флейм-графа отображает стеки вызовов в виде вложенных прямоугольников, ширина которых пропорциональна затраченному времени. На боковой панели отображается статистика выполнения, метрики GIL и горячие точки функций.702703[Попробуйте интерактивный пример](https://python-all.ru/3.15/_static/tachyon-example-flamegraph.html)!704705Если не указан выходной файл, профилировщик генерирует имя файла на основе идентификатора процесса (например, `flamegraph.12345.html`).706707Сгенерированный HTML-файл не требует внешних зависимостей и может быть открыт прямо в веб-браузере. Визуализация показывает стеки вызовов в виде вложенных прямоугольников, ширина которых пропорциональна затраченному времени. При наведении на прямоугольник отображаются подробности о функции, включая контекст исходного кода, а щелчок увеличивает соответствующую часть дерева вызовов.708709Интерфейс флейм-графа включает:710711- Боковую панель, показывающую сводку профиля, статистику потоков, метрики эффективности сэмплирования (см. [Эффективность сэмплирования](https://python-all.ru/3.15/library/profiling.sampling.html#sampling-efficiency)) и основные горячие точки функций712- Функцию поиска, поддерживающую как сопоставление имён функций, так и шаблоны `file.py:42` строк713- Фильтрацию по потокам через выпадающее меню714- Переключатель тёмной/светлой темы (настройка сохраняется между сеансами)715- Экспорт в SVG для сохранения текущего вида716717В разделе статистики потоков отображаются метрики поведения во время выполнения:718719- **GIL Held**: процент выборок, в которых поток удерживал глобальную блокировку интерпретатора (активно выполнял код Python)720- **GIL Released**: процент выборок, в которых ни один поток не удерживал GIL721- **Waiting GIL**: процент выборок, в которых поток ожидал захвата GIL722- **GC**: процент выборок во время сборки мусора723724Эти статистики помогают выявить конкуренцию за GIL и понять, как время распределяется между выполнением Python, нативным кодом и ожиданием.725726Флейм-графы особенно эффективны для выявления глубоких стеков вызовов и понимания иерархической структуры потребления времени. Широкие прямоугольники вверху указывают на функции, которые потребляют значительное время либо напрямую, либо через вызываемые ими функции.727728#### Дифференциальные флейм-графы729730Дифференциальные флейм-графы сравнивают два профилировочных запуска, чтобы выделить места, где изменилась производительность. Это помогает выявить регрессии, внесённые изменениями кода, и проверить, что оптимизации достигли желаемого эффекта:731732```sh733# Захватить базовый профиль734python -m profiling.sampling run --binary -o baseline.bin script.py735736# После изменения кода сгенерировать дифференциальный граф пламени737python -m profiling.sampling run --diff-flamegraph baseline.bin -o diff.html script.py738```739740Визуализация отображает текущий профиль: ширина фреймов показывает текущее потребление времени, а цвет указывает на то, как изменилась каждая функция относительно базового профиля.741742**Цветовая кодировка**:743744- **Красный**: функции, потребляющие больше времени (регрессии). Светлые оттенки указывают на незначительное увеличение, тёмные – на серьёзные регрессии.745- **Синий**: функции, потребляющие меньше времени (улучшения). Светлые оттенки – для незначительных уменьшений, тёмные – для значительных ускорений.746- **Серый**: минимальные или нулевые изменения.747- **Фиолетовый**: новые функции, отсутствовавшие в базовом профиле.748749Цвета фреймов показывают изменения в **прямом времени** (время, когда функция находилась на вершине стека и активно выполнялась), а не в кумулятивном времени, включающем вызываемые функции. При наведении на фрейм отображаются детали сравнения: время в базовом профиле, текущее время и процент изменения.750751Некоторые пути вызовов могут полностью исчезнуть между профилями. Они называются **элидированными стеками** и возникают, когда оптимизации устраняют пути кода или некоторые ветки перестают выполняться. Если есть элидированные стеки, появляется переключатель, позволяющий переключаться между основным дифференциальным представлением и представлением только элидированных путей, показывающим удалённые пути (окрашены фиолетовым).752753### Формат Gecko754755Формат Gecko ([`--gecko`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-gecko)) создаёт JSON-вывод, совместимый с Firefox Profiler:756757```sh758python -m profiling.sampling run --gecko script.py759python -m profiling.sampling run --gecko -o profile.json script.py760```761762[Firefox Profiler](https://python-all.ru/3.15/library/profiling.sampling.html) – это сложный веб-инструмент, изначально созданный для профилирования самого Firefox. Он предоставляет возможности, выходящие за рамки базовых флейм-графов, включая временную шкалу, исследование дерева вызовов и визуализацию маркеров. Инструкции по использованию см. в [документации Firefox Profiler](https://python-all.ru/3.15/library/profiling.sampling.html).763764Чтобы использовать результат, откройте Firefox Profiler в браузере и загрузите JSON-файл. Профилировщик работает полностью на стороне клиента, поэтому ваши данные профилирования никогда не покидают вашу машину.765766Формат Gecko автоматически собирает дополнительные метаданные о состоянии GIL и активности ЦП, что позволяет использовать функции анализа, специфичные для модели потоков Python. Профилировщик испускает интервальные маркеры, которые отображаются в виде цветных полос на временной шкале Firefox Profiler:767768- **Маркеры GIL**: показывают, когда потоки удерживают или освобождают GIL769- **Маркеры ЦП**: показывают, когда потоки выполняются на ЦП, а когда простаивают770- **Маркеры типа кода**: различают код Python и нативный код (расширения C)771- **Маркеры GC**: указывают на активность сборщика мусора772773По этой причине опция [`--mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-mode) недоступна в формате Gecko; все соответствующие данные собираются автоматически.774775[![Представление дерева вызовов профилировщика Firefox](https://python-all.ru/3.15/_images/tachyon-gecko-calltree.png)](https://python-all.ru/3.15/_images/tachyon-gecko-calltree.png)776777В представлении «Дерево вызовов» отображается полная иерархия вызовов с количеством сэмплов и процентами. На боковой панели показываются детальные статистики для выбранной функции, включая время выполнения и распределение сэмплов.778779[![Представление флейм-графа профилировщика Firefox](https://python-all.ru/3.15/_images/tachyon-gecko-flamegraph.png)](https://python-all.ru/3.15/_images/tachyon-gecko-flamegraph.png)780781Визуализация флейм-графа показывает стеки вызовов в виде вложенных прямоугольников. Имена функций видны в иерархии вызовов.782783[![Диаграмма маркеров профилировщика Firefox с кодами операций](https://python-all.ru/3.15/_images/tachyon-gecko-opcodes.png)](https://python-all.ru/3.15/_images/tachyon-gecko-opcodes.png)784785Диаграмма маркеров отображает интервальные маркеры, включая состояние ЦП, статус GIL и коды операций. При включённом `--opcodes` инструкции байткода, такие как `BINARY_OP_ADD_FLOAT`, `CALL_PY_EXACT_ARGS` и `CALL_LIST_APPEND`, появляются в качестве маркеров, показывающих выполнение во времени.786787### Формат тепловой карты788789Формат тепловой карты ([`--heatmap`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-heatmap)) создаёт интерактивную HTML-визуализацию, показывающую количество сэмплов на уровне строк исходного кода:790791```sh792python -m profiling.sampling run --heatmap script.py793python -m profiling.sampling run --heatmap -o my_heatmap script.py794```795796[![Визуализация тепловой карты Tachyon](https://python-all.ru/3.15/_images/tachyon-heatmap.png)](https://python-all.ru/3.15/_images/tachyon-heatmap.png)797798Тепловая карта накладывает количество сэмплов прямо на исходный код. Строки окрашиваются от холодных (мало сэмплов) до горячих (много сэмплов). Кнопки навигации (▲▼) позволяют переходить между вызывающими и вызываемыми функциями.799800В отличие от других форматов, которые создают один файл, вывод тепловой карты формирует каталог, содержащий HTML-файлы для каждого профилируемого исходного файла. Если путь вывода не указан, каталог называется `heatmap_PID`.801802Визуализация тепловой карты отображает исходный код с цветовым градиентом, показывающим, сколько сэмплов было собрано для каждой строки. Горячие строки (много сэмплов) отображаются тёплыми цветами, а холодные (мало или нет сэмплов) – холодными. Такой вид помогает точно определить, какие строки кода ответственны за затраты времени.803804Интерфейс тепловой карты предоставляет несколько интерактивных возможностей:805806- **Режимы окраски**: переключение между «Собственное время» (непосредственное выполнение) и «Общее время» (суммарное, включая время в вызываемых функциях)807- **Фильтрация холодного кода**: показывать все строки или только строки с сэмплами808- **Навигация по графу вызовов**: каждая строка показывает кнопки навигации (▲ для вызывающих, ▼ для вызываемых), позволяющие проследить пути выполнения в коде. Когда несколько функций вызывают или вызываются из строки, появляется меню со всеми вариантами и количеством сэмплов.809- **Миникарта прокрутки**: вертикальный обзор, показывающий распределение тепла по всему файлу810- **Иерархический индекс**: файлы, сгруппированные по типу (stdlib, site-packages, project) с суммарным количеством сэмплов в каждой папке811- **Тёмная/светлая тема**: переключение с сохранением предпочтения между сеансами812- **Ссылки на строки**: клик по номеру строки для создания ссылок, которыми можно поделиться813814Если включено профилирование на уровне опкодов с помощью [`--opcodes`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-opcodes), каждую горячую строку можно развернуть, чтобы увидеть, какие инструкции байт-кода потребовали времени:815816[![Тепловая карта с развёрнутой панелью байт-кода](https://python-all.ru/3.15/_images/tachyon-heatmap-with-opcodes.png)](https://python-all.ru/3.15/_images/tachyon-heatmap-with-opcodes.png)817818При разворачивании горячей строки показываются выполненные инструкции байт-кода, включая специализированные варианты. Панель отображает количество сэмплов на инструкцию и общий процент специализации для строки.819820[Попробуйте интерактивный пример](https://python-all.ru/3.15/_static/tachyon-example-heatmap.html)!821822Тепловые карты особенно полезны, когда известно, в каком файле находится проблема производительности, но нужно определить конкретные строки. Многие разработчики предпочитают этот формат, потому что он напрямую сопоставляется с исходным кодом, что упрощает чтение и навигацию. Для небольших скриптов и целенаправленного анализа тепловые карты дают интуитивно понятное представление, показывающее, где именно тратится время, без необходимости интерпретации иерархических визуализаций.823824### Бинарный формат825826Бинарный формат ([`--binary`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-binary)) создаёт компактный бинарный файл для эффективного хранения данных профилирования:827828```sh829python -m profiling.sampling run --binary -o profile.bin script.py830python -m profiling.sampling attach --binary -o profile.bin 12345831```832833Параметр [`--compression`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-compression) управляет сжатием данных:834835- `auto` (по умолчанию): использовать сжатие zstd, если доступно, иначе без сжатия836- `zstd`: принудительно использовать сжатие zstd (требуется поддержка [`compression.zstd`](https://python-all.ru/3.15/library/compression.zstd.html#module-compression.zstd))837- `none`: отключить сжатие838839```sh840python -m profiling.sampling run --binary --compression=zstd -o profile.bin script.py841```842843Для анализа бинарных профилей используйте [команду replay](https://python-all.ru/3.15/library/profiling.sampling.html#replay-command), чтобы преобразовать их в другие форматы, например, flame graphs или вывод pstats.844845## Рабочий процесс записи и воспроизведения846847Бинарный формат в сочетании с командой replay обеспечивает рабочий процесс записи и воспроизведения, который разделяет сбор данных и анализ. Вместо создания визуализаций во время профилирования сырые данные сохраняются в компактный бинарный файл, а затем преобразуются в разные форматы.848849У этого подхода три основных преимущества:850851- Сэмплирование выполняется быстрее, поскольку построение структур данных для визуализации откладывается до момента воспроизведения.852- Один бинарный файл с данными можно преобразовать в несколько форматов вывода без повторного профилирования: pstats для быстрого обзора, flame graph для визуального исследования, тепловую карту для детализации на уровне строк.853- Бинарные файлы компактны, и ими легко делиться с коллегами, которые могут преобразовать их в предпочитаемый формат.854855Типичный рабочий процесс:856857```sh858# Захватить профиль в продакшене или во время тестов859python -m profiling.sampling attach --binary -o profile.bin 12345860861# Позже проанализировать в различных форматах862python -m profiling.sampling replay profile.bin863python -m profiling.sampling replay --flamegraph -o profile.html profile.bin864python -m profiling.sampling replay --heatmap -o heatmap profile.bin865```866867## Режим реального времени868869Режим реального времени ([`--live`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-live)) предоставляет терминальный просмотр данных профилирования в реальном времени, аналогично команде `top` для системных процессов:870871```sh872python -m profiling.sampling run --live script.py873python -m profiling.sampling attach --live 12345874```875876[![Режим реального времени Tachyon, показывающий все потоки](https://python-all.ru/3.15/_images/tachyon-live-mode-2.gif)](https://python-all.ru/3.15/_images/tachyon-live-mode-2.gif)877878В режиме реального времени отображается статистика профилирования в реальном времени, объединяющая данные из нескольких потоков в многопоточном приложении.879880Дисплей непрерывно обновляется по мере поступления новых выборок, показывая текущие самые «горячие» функции. Этот режим требует модуля [`curses`](https://python-all.ru/3.15/library/curses.html#module-curses), который доступен в Unix-подобных системах, но не в Windows. Терминал должен быть шириной не менее 60 столбцов и высотой не менее 12 строк; в больших терминалах отображается больше столбцов.881882В заголовке отображаются три самые «горячие» функции, метрики эффективности выборки и статистика состояния потоков (процент удержания GIL, загрузка ЦП, время GC). Основная таблица показывает статистику функций; текущий столбец сортировки обозначается стрелкой (▼).883884Если [`--opcodes`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-opcodes) включён, под основной таблицей появляется дополнительная панель с опкодами, показывающая статистику на уровне инструкций для выбранной функции. Эта панель отображает, какие инструкции байткода выполняются чаще всего, включая специализированные варианты и их базовые опкоды.885886[![Живой режим Tachyon с панелью опкодов](https://python-all.ru/3.15/_images/tachyon-live-mode-1.gif)](https://python-all.ru/3.15/_images/tachyon-live-mode-1.gif)887888Режим реального времени с включённым `--opcodes` показывает панель опкодов с разбивкой инструкций байткода для выбранной функции.889890### Команды клавиатуры891892В режиме реального времени команды клавиатуры управляют отображением:893894**`q`**895896Выйти из профилировщика и вернуться в оболочку.897898**`s` / `S`**899900Переключение порядка сортировки вперёд/назад (количество выборок, процент, общее время, накопленный процент, накопленное время).901902**`p`**903904Приостановить или возобновить обновление дисплея. Во время паузы выборка продолжается в фоне, так что можно заморозить просмотр для изучения результатов, не останавливая сбор данных.905906**`r`**907908Сбросить всю статистику и начать заново. Эта команда отключается после завершения профилирования, чтобы предотвратить случайную потерю данных.909910**`/`**911912Вход в режим фильтрации для поиска функций по имени. Фильтр использует поиск подстроки без учёта регистра по имени файла и функции. Введите шаблон и нажмите Enter для применения или Escape для отмены. Глоб-шаблоны и регулярные выражения не поддерживаются.913914**`c`**915916Очистить текущий фильтр и снова показать все функции.917918**`t`**919920Переключение между просмотром всех потоков вместе или статистикой по каждому потоку. В режиме «по потокам» появляется счётчик потоков (например, `1/4`), показывающий текущую позицию среди доступных потоков.921922**`←` `→` или `↑` `↓`**923924В режиме просмотра по потокам переключение между потоками. Переход зациклен: от последнего потока к первому и наоборот.925926**`+` / `-`**927928Увеличить или уменьшить частоту обновления дисплея. Диапазон: от 0,05 секунды (20 Гц, очень отзывчиво) до 1,0 секунды (1 Гц, меньшая нагрузка). Более высокая частота обновления потребляет больше ЦП. По умолчанию – 0,1 секунды (10 Гц).929930**`x`**931932Включение/выключение индикаторов тренда, показывающих, становятся ли функции «горячее» или «холоднее» со временем. При включении увеличивающиеся метрики отображаются зелёным, а уменьшающиеся – красным; сравнивается каждое обновление с предыдущим.933934**`h` или `?`**935936Показать экран справки со всеми доступными командами.937938**`j` / `k` (или `Up` / `Down`)**939940Навигация по записям опкодов на панели опкодов (когда `--opcodes` включён). Эти клавиши прокручивают статистику на уровне инструкций для выбранной функции.941942Когда профилирование завершается (истекает длительность или завершается целевой процесс), на дисплее появляется баннер «PROFILING COMPLETE» (ПРОФИЛИРОВАНИЕ ЗАВЕРШЕНО) и фиксируются окончательные результаты. Можно по-прежнему перемещаться, сортировать и фильтровать результаты, прежде чем нажать `q` для выхода.943944Режим реального времени несовместим с опциями формата вывода ([`--collapsed`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-collapsed), [`--flamegraph`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-flamegraph) и т. д.), поскольку он использует интерактивный терминальный интерфейс, а не создаёт файловый вывод.945946## Профилирование с учётом асинхронности947948Для программ, использующих [`asyncio`](https://python-all.ru/3.15/library/asyncio.html#module-asyncio), профилировщик предлагает режим с учётом асинхронности ([`--async-aware`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-async-aware)), который восстанавливает стеки вызовов на основе структуры задач, а не необработанных фреймов Python:949950```sh951python -m profiling.sampling run --async-aware async_script.py952```953954Стандартное профилирование асинхронного кода может сбивать с толку, поскольку физический стек вызовов часто показывает внутренности цикла событий, а не логический поток корутин. Режим с учётом асинхронности решает эту проблему, отслеживая, какая задача выполняется, и представляя стеки, отражающие цепочку `await`.955956```python957import asyncio958959async def fetch(url):960    await asyncio.sleep(0.1)961    return url962963async def main():964    for _ in range(50):965        await asyncio.gather(fetch("a"), fetch("b"), fetch("c"))966967if __name__ == "__main__":968    asyncio.run(main())969```970971```sh972python -m profiling.sampling run --async-aware --flamegraph -o out.html script.py973```974975> **Примечание**976>977> Для профилирования с учётом асинхронности требуется, чтобы в целевом процессе был загружен модуль [`asyncio`](https://python-all.ru/3.15/library/asyncio.html#module-asyncio). Если вы профилируете скрипт до того, как он импортирует asyncio, режим с учётом асинхронности не сможет захватывать информацию о задачах.978979### Режимы асинхронности980981Опция [`--async-mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-async-mode) управляет тем, какие задачи отображаются в профиле:982983```sh984python -m profiling.sampling run --async-aware --async-mode=running async_script.py985python -m profiling.sampling run --async-aware --async-mode=all async_script.py986```987988С [`--async-mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-async-mode)`=running` (по умолчанию) профилируется только задача, выполняющаяся в данный момент на ЦП. Это показывает, где ваша программа активно тратит время, и является типичным выбором для анализа производительности.989990С [`--async-mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-async-mode)`=all` также включаются приостановленные задачи (ожидающие ввода-вывода, блокировок или других задач). Этот режим полезен для понимания того, чего ждёт ваша программа, но создаёт более крупные профили, поскольку каждая приостановленная задача появляется в каждой выборке.991992### Маркеры задач и восстановление стека993994В профилях с поддержкой async вы увидите кадры `<task>`, которые отмечают границы между задачами asyncio. Это синтетические кадры, вставленные профилировщиком для отображения структуры задач. В этих кадрах имя задачи отображается как имя функции.995996Когда задача ожидает другую задачу, профилировщик восстанавливает логическую цепочку вызовов, следуя по связям `await`. Только «листовые» задачи (задачи, которые в данный момент не ожидаются другой задачей) создают собственные записи в стеке. Задачи, ожидаемые другими задачами, отображаются как часть стека ожидающей их задачи.997998Если у задачи несколько ожидающих (ромбовидный паттерн в графе задач), профилировщик детерминированно выбирает одного родителя и помечает маркер задачи количеством родителей, например `MyTask (2 parents)`. Это указывает на существование альтернативных путей выполнения, которые не показаны в данном конкретном стеке.9991000### Ограничения параметров10011002Режим с поддержкой async использует другой механизм восстановления стека и несовместим с: [`--native`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-native), [`--no-gc`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-no-gc), [`--all-threads`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-a), а также [`--mode`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-mode)`=cpu` или `--mode``=gil`.10031004## Интерфейс командной строки10051006Полный интерфейс командной строки для справки.10071008### Глобальные параметры10091010#### `run`10111012Запуск и профилирование скрипта или модуля Python.10131014#### `attach`10151016Подключение к запущенному процессу по PID и его профилирование.10171018#### `dump`10191020Вывод однократного снимка стека Python запущенного процесса.10211022#### `replay`10231024Преобразование бинарного файла профиля в другой формат вывода.10251026### Параметры dump10271028Следующие параметры применяются к подкоманде `dump`:10291030#### `-a, --all-threads`10311032Дамп всех потоков в целевом процессе вместо только главного потока.10331034#### `--native`10351036Включать кадры `<native>` для кода не на Python.10371038#### `--no-gc`10391040Исключить кадры `<GC>` для активной сборки мусора.10411042#### `--opcodes`10431044Показывать имена опкодов байт-кода, если доступны.10451046#### `--async-aware`10471048Восстанавливать стек через границы `await` для приложений asyncio.10491050#### `--async-mode <mode>`10511052Режим асинхронного стека: `running` (только запущенная задача) или `all` (все задачи, включая ожидающие). По умолчанию `all` для `dump`. Требует [`--async-aware`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-async-aware).10531054#### `--blocking`10551056Приостанавливать все потоки в целевом процессе при чтении стека.10571058### Параметры сэмплирования10591060#### `-r <rate>, --sampling-rate <rate>`10611062Частота дискретизации (например, `10000`, `10khz`, `10k`). По умолчанию: `1khz`.10631064#### `-d <seconds>, --duration <seconds>`10651066Длительность профилирования в секундах. По умолчанию: до завершения.10671068#### `-a, --all-threads`10691070Выполнять семплирование всех потоков, а не только основного.10711072#### `--realtime-stats`10731074Отображать статистику семплирования во время профилирования.10751076#### `--native`10771078Включать кадры `<native>` для кода не на Python.10791080#### `--no-gc`10811082Исключать кадры `<GC>` для сборки мусора.10831084#### `--async-aware`10851086Включить профилирование с учётом асинхронности для программ asyncio.10871088#### `--opcodes`10891090Собирает информацию об опкодах байткода для профилирования на уровне инструкций. Показывает, какие инструкции байткода выполняются, включая специализации. Совместимо только с форматами `--live`, `--flamegraph`, `--heatmap` и `--gecko`.10911092#### `--subprocesses`10931094Также профилировать подпроцессы. Каждый подпроцесс получает собственный экземпляр профилировщика и выходной файл. Несовместимо с `--live`.10951096#### `--blocking`10971098Приостанавливать целевой процесс при каждом семпле. Это обеспечивает согласованные стек-трейсы ценой замедления цели. Используйте с большими интервалами (1000 мкс и выше), чтобы минимизировать влияние. См. [Режим блокировки](https://python-all.ru/3.15/library/profiling.sampling.html#blocking-mode) подробнее.10991100### Параметры режима11011102#### `--mode <mode>`11031104Режим семплирования: `wall` (по умолчанию), `cpu`, `gil` или `exception`. Режимы `cpu`, `gil` и `exception` несовместимы с `--async-aware`.11051106#### `--async-mode <mode>`11071108Режим асинхронного профилирования: `running` (по умолчанию) или `all`. Требуется `--async-aware`.11091110### Параметры вывода11111112#### `--pstats`11131114Генерировать статистику pstats. Это значение по умолчанию. При выводе в stdout результат – текстовая таблица; с [`-o`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-o) это бинарный файл pstats.11151116#### `--collapsed`11171118Генерировать свернутый формат стека для внешних инструментов построения пламенных графов.11191120#### `--flamegraph`11211122Генерировать самодостаточный HTML-пламенный граф.11231124#### `--diff-flamegraph <baseline.bin>`11251126Создать дифференциальную флейм-графию для сравнения с базовым бинарным профилем.11271128#### `--gecko`11291130Создать вывод в формате Gecko JSON для Firefox Profiler.11311132#### `--heatmap`11331134Создать HTML-тепловую карту с количеством сэмплов по строкам.11351136#### `--binary`11371138Создать высокопроизводительный бинарный формат для последующего преобразования с помощью команды `replay`.11391140#### `--compression <type>`11411142Сжатие для бинарного формата: `auto` (использовать zstd, если доступен, по умолчанию), `zstd` или `none`.11431144#### `-o <path>, --output <path>`11451146Путь к выходному файлу или каталогу. Поведение по умолчанию зависит от формата: [`--pstats`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-pstats) выводит текстовую таблицу в stdout, а `-o` записывает бинарный файл pstats. Другие форматы создают файл с именем `<format>_<PID>.<ext>` (например, `flamegraph_12345.html`). [`--heatmap`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-heatmap) создает каталог с именем `heatmap_<PID>`.11471148#### `--browser`11491150Автоматически открывать HTML-вывод ([`--flamegraph`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-flamegraph) и [`--heatmap`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-heatmap)) в веб-браузере по умолчанию после создания. При профилировании с помощью [`--subprocesses`](https://python-all.ru/3.15/library/profiling.sampling.html#cmdoption-profiling.sampling-subprocesses) браузер открывается только в главном процессе; выводы подпроцессов никогда не открываются автоматически.11511152### Параметры отображения pstats11531154Эти параметры применяются только к выводу в формате pstats.11551156#### `--sort <key>`11571158Порядок сортировки: `nsamples`, `tottime`, `cumtime`, `sample-pct`, `cumul-pct`, `nsamples-cumul` или `name`. По умолчанию: `nsamples`.11591160#### `-l <count>, --limit <count>`11611162Максимальное количество записей для отображения. По умолчанию: 15.11631164#### `--no-summary`11651166Исключить из вывода разделы «Легенда» и «Сводка интересующих функций».11671168### Параметры команды запуска11691170#### `-m, --module`11711172Рассматривать целевой объект как имя модуля, а не как путь к скрипту.11731174#### `--live`11751176Запустить интерактивный терминальный интерфейс вместо пакетного профилирования.11771178> **См. также**1179>1180> **[`profiling`](https://python-all.ru/3.15/library/profiling.html#module-profiling)**1181>1182> Обзор инструментов профилирования Python и рекомендации по выбору профилировщика.1183>1184> **[`profiling.tracing`](https://python-all.ru/3.15/library/profiling.tracing.html#module-profiling.tracing)**1185>1186> Детерминированный трассирующий профилировщик для точного подсчета вызовов и измерения времени.1187>1188> **[`pstats`](https://python-all.ru/3.15/library/pstats.html#module-pstats)**1189>1190> Статистический анализ данных профиля.1191>1192> **[Firefox Profiler](https://python-all.ru/3.15/library/profiling.sampling.html)**1193>1194> Веб-профилировщик, принимающий вывод в формате Gecko. Подробности использования – в [документации](https://python-all.ru/3.15/library/profiling.sampling.html).1195>1196> **[FlameGraph](https://python-all.ru/3.15/library/profiling.sampling.html)**1197>1198> Инструменты для генерации флейм-графов из свёрнутого формата стека.1199