perf_profiling.md
1> **Источник:** https://python-all.ru/3.12/howto/perf_profiling.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Поддержка профилировщика `perf` в Linux средствами Python89**автор:**1011Pablo Galindo1213[Профилировщик perf в Linux](https://python-all.ru/3.12/howto/perf_profiling.html) – это очень мощный инструмент, позволяющий профилировать и получать информацию о производительности вашего приложения. `perf` также обладает очень живой экосистемой инструментов, помогающих анализировать данные, которые он генерирует.1415Основная проблема использования профилировщика `perf` с приложениями Python заключается в том, что `perf` получает информацию только о нативных символах, то есть именах функций и процедур, написанных на C. Это означает, что имена и имена файлов функций Python в вашем коде не будут отображаться в выводе `perf`.1617Since Python 3.12, the interpreter can run in a special mode that allows Python functions to appear in the output of the `perf` profiler. When this mode is enabled, the interpreter will interpose a small piece of code compiled on the fly before the execution of every Python function and it will teach `perf` the relationship between this piece of code and the associated Python function using [perf map files](https://python-all.ru/3.12/c-api/perfmaps.html).1819> **Примечание**20>21> Поддержка профилировщика `perf` в настоящее время доступна только для Linux на отдельных архитектурах. Проверьте вывод шага сборки `configure` или вывод `python -m sysconfig | grep HAVE_PERF_TRAMPOLINE`, чтобы узнать, поддерживается ли ваша система.2223Например, рассмотрим следующий скрипт:2425```python26def foo(n):27 result = 028 for _ in range(n):29 result += 130 return result3132def bar(n):33 foo(n)3435def baz(n):36 bar(n)3738if __name__ == "__main__":39 baz(1000000)40```4142Мы можем запустить `perf` для сбора выборок стеков ЦП с частотой 9999 Гц:4344```console45$ perf record -F 9999 -g -o perf.data python my_script.py46```4748Затем мы можем использовать `perf report` для анализа данных:4950```console51$ perf report --stdio -n -g5253# Children Self Samples Command Shared Object Symbol54# ........ ........ ............ .......... .................. ..........................................55#56 91.08% 0.00% 0 python.exe python.exe [.] _start57 |58 ---_start59 |60 --90.71%--__libc_start_main61 Py_BytesMain62 |63 |--56.88%--pymain_run_python.constprop.064 | |65 | |--56.13%--_PyRun_AnyFileObject66 | | _PyRun_SimpleFileObject67 | | |68 | | |--55.02%--run_mod69 | | | |70 | | | --54.65%--PyEval_EvalCode71 | | | _PyEval_EvalFrameDefault72 | | | PyObject_Vectorcall73 | | | _PyEval_Vector74 | | | _PyEval_EvalFrameDefault75 | | | PyObject_Vectorcall76 | | | _PyEval_Vector77 | | | _PyEval_EvalFrameDefault78 | | | PyObject_Vectorcall79 | | | _PyEval_Vector80 | | | |81 | | | |--51.67%--_PyEval_EvalFrameDefault82 | | | | |83 | | | | |--11.52%--_PyLong_Add84 | | | | | |85 | | | | | |--2.97%--_PyObject_Malloc86...87```8889Как видите, функции Python не отображаются в выводе, только `_PyEval_EvalFrameDefault` (функция, вычисляющая байт-код Python). К сожалению, это не очень полезно, потому что все функции Python используют одну и ту же функцию C для вычисления байт-кода, поэтому мы не можем узнать, какая функция Python соответствует какой функции вычисления байт-кода.9091Вместо этого, если мы запустим тот же эксперимент с включённой поддержкой `perf`, мы получим:9293```console94$ perf report --stdio -n -g9596# Children Self Samples Command Shared Object Symbol97# ........ ........ ............ .......... .................. .....................................................................98#99 90.58% 0.36% 1 python.exe python.exe [.] _start100 |101 ---_start102 |103 --89.86%--__libc_start_main104 Py_BytesMain105 |106 |--55.43%--pymain_run_python.constprop.0107 | |108 | |--54.71%--_PyRun_AnyFileObject109 | | _PyRun_SimpleFileObject110 | | |111 | | |--53.62%--run_mod112 | | | |113 | | | --53.26%--PyEval_EvalCode114 | | | py::<module>:/src/script.py115 | | | _PyEval_EvalFrameDefault116 | | | PyObject_Vectorcall117 | | | _PyEval_Vector118 | | | py::baz:/src/script.py119 | | | _PyEval_EvalFrameDefault120 | | | PyObject_Vectorcall121 | | | _PyEval_Vector122 | | | py::bar:/src/script.py123 | | | _PyEval_EvalFrameDefault124 | | | PyObject_Vectorcall125 | | | _PyEval_Vector126 | | | py::foo:/src/script.py127 | | | |128 | | | |--51.81%--_PyEval_EvalFrameDefault129 | | | | |130 | | | | |--13.77%--_PyLong_Add131 | | | | | |132 | | | | | |--3.26%--_PyObject_Malloc133```134135## Как включить поддержку профилирования `perf`136137Поддержку профилирования `perf` можно включить либо с самого начала, используя переменную окружения [`PYTHONPERFSUPPORT`](https://python-all.ru/3.12/using/cmdline.html#envvar-PYTHONPERFSUPPORT) или параметр [`-X perf`](https://python-all.ru/3.12/using/cmdline.html#cmdoption-X), либо динамически с помощью [`sys.activate_stack_trampoline()`](https://python-all.ru/3.12/library/sys.html#sys.activate_stack_trampoline) и [`sys.deactivate_stack_trampoline()`](https://python-all.ru/3.12/library/sys.html#sys.deactivate_stack_trampoline).138139Функции `sys` имеют приоритет над параметром `-X`, а параметр `-X` имеет приоритет над переменной окружения.140141Пример с использованием переменной окружения:142143```console144$ PYTHONPERFSUPPORT=1 python script.py145$ perf report -g -i perf.data146```147148Пример с использованием параметра `-X`:149150```console151$ python -X perf script.py152$ perf report -g -i perf.data153```154155Пример с использованием API [`sys`](https://python-all.ru/3.12/library/sys.html#module-sys) в файле `example.py`:156157```python158import sys159160sys.activate_stack_trampoline("perf")161do_profiled_stuff()162sys.deactivate_stack_trampoline()163164non_profiled_stuff()165```166167…затем:168169```console170$ python ./example.py171$ perf report -g -i perf.data172```173174## Как получить наилучшие результаты175176Для достижения наилучших результатов Python следует компилировать с `CFLAGS="-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"`, так как это позволяет профилировщикам выполнять размотку стека, используя только указатель кадра, а не отладочную информацию DWARF. Это связано с тем, что код, вставляемый для поддержки `perf`, генерируется динамически и не имеет доступной отладочной информации DWARF.177178Вы можете проверить, скомпилирована ли ваша система с этим флагом, выполнив:179180```console181$ python -m sysconfig | grep 'no-omit-frame-pointer'182```183184Если вы не видите никакого вывода, это означает, что ваш интерпретатор не был скомпилирован с указателями фреймов, и поэтому он может не отображать функции Python в выводе `perf`.185