Поддержка Python для профилировщика Linux perf
- автор:
-
Pablo Galindo
Профилировщик Linux perf — это очень мощный инструмент, позволяющий профилировать приложение и получать сведения о его производительности. perf также располагает развитой экосистемой инструментов, помогающих анализировать создаваемые им данные.
Основная проблема использования профилировщика perf для приложений на Python заключается в том, что perf получает сведения только о нативных символах, то есть именах функций и процедур, написанных на C. Это означает, что имена функций Python в вашем коде и имена файлов не появятся в выводе perf.
Начиная с Python 3.12 интерпретатор может работать в специальном режиме, который позволяет отображать функции Python в выводе профилировщика perf. При включении этого режима перед выполнением каждой функции Python интерпретатор динамически компилирует и подставляет небольшой фрагмент кода, а затем сообщает perf о связи между этим фрагментом кода и соответствующей функцией Python с помощью файлов карт perf.
Примечание
Поддержка профилировщика perf в настоящее время доступна только в Linux для некоторых архитектур. Проверьте вывод этапа сборки configure или вывод python -m sysconfig | grep HAVE_PERF_TRAMPOLINE, чтобы узнать, поддерживается ли ваша система.
Например, рассмотрим следующий скрипт:
def foo(n):
result = 0
for _ in range(n):
result += 1
return result
def bar(n):
foo(n)
def baz(n):
bar(n)
if __name__ == "__main__":
baz(1000000)
Мы можем запустить perf, чтобы собирать трассировки стека ЦП с частотой 9999 Гц:
$ perf record -F 9999 -g -o perf.data python my_script.py
Затем можно использовать perf report для анализа данных:
$ perf report --stdio -n -g
# Children Self Samples Command Shared Object Symbol
# ........ ........ ............ .......... .................. ..........................................
#
91.08% 0.00% 0 python.exe python.exe [.] _start
|
---_start
|
--90.71%--__libc_start_main
Py_BytesMain
|
|--56.88%--pymain_run_python.constprop.0
| |
| |--56.13%--_PyRun_AnyFileObject
| | _PyRun_SimpleFileObject
| | |
| | |--55.02%--run_mod
| | | |
| | | --54.65%--PyEval_EvalCode
| | | _PyEval_EvalFrameDefault
| | | PyObject_Vectorcall
| | | _PyEval_Vector
| | | _PyEval_EvalFrameDefault
| | | PyObject_Vectorcall
| | | _PyEval_Vector
| | | _PyEval_EvalFrameDefault
| | | PyObject_Vectorcall
| | | _PyEval_Vector
| | | |
| | | |--51.67%--_PyEval_EvalFrameDefault
| | | | |
| | | | |--11.52%--_PyLong_Add
| | | | | |
| | | | | |--2.97%--_PyObject_Malloc
...
Как видите, функции Python в выводе не отображаются, появляется только _PyEval_EvalFrameDefault (функция, которая выполняет байт-код Python). К сожалению, это не очень полезно, поскольку все функции Python используют одну и ту же функцию C для выполнения байт-кода, поэтому невозможно определить, какой функции Python соответствует та или иная функция выполнения байт-кода.
Однако если провести тот же эксперимент с включённой поддержкой perf, результат будет таким:
$ perf report --stdio -n -g
# Children Self Samples Command Shared Object Symbol
# ........ ........ ............ .......... .................. .....................................................................
#
90.58% 0.36% 1 python.exe python.exe [.] _start
|
---_start
|
--89.86%--__libc_start_main
Py_BytesMain
|
|--55.43%--pymain_run_python.constprop.0
| |
| |--54.71%--_PyRun_AnyFileObject
| | _PyRun_SimpleFileObject
| | |
| | |--53.62%--run_mod
| | | |
| | | --53.26%--PyEval_EvalCode
| | | py::<module>:/src/script.py
| | | _PyEval_EvalFrameDefault
| | | PyObject_Vectorcall
| | | _PyEval_Vector
| | | py::baz:/src/script.py
| | | _PyEval_EvalFrameDefault
| | | PyObject_Vectorcall
| | | _PyEval_Vector
| | | py::bar:/src/script.py
| | | _PyEval_EvalFrameDefault
| | | PyObject_Vectorcall
| | | _PyEval_Vector
| | | py::foo:/src/script.py
| | | |
| | | |--51.81%--_PyEval_EvalFrameDefault
| | | | |
| | | | |--13.77%--_PyLong_Add
| | | | | |
| | | | | |--3.26%--_PyObject_Malloc
Как включить поддержку профилирования perf
Поддержку профилирования perf можно включить при запуске с помощью переменной окружения PYTHONPERFSUPPORT или параметра -X perf, а также динамически с помощью sys.activate_stack_trampoline() и sys.deactivate_stack_trampoline().
Функции sys имеют приоритет над параметром -X, а параметр -X имеет приоритет над переменной окружения.
Пример с использованием переменной окружения:
$ PYTHONPERFSUPPORT=1 python script.py $ perf report -g -i perf.data
Пример с использованием параметра -X:
$ python -X perf script.py $ perf report -g -i perf.data
Пример использования API sys в файле example.py:
import sys
sys.activate_stack_trampoline("perf")
do_profiled_stuff()
sys.deactivate_stack_trampoline()
non_profiled_stuff()
…затем:
$ python ./example.py $ perf report -g -i perf.data
Как добиться наилучших результатов
Для достижения наилучших результатов Python следует собирать с CFLAGS="-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer", поскольку это позволяет профилировщикам выполнять раскрутку стека, используя только указатель кадра, а не отладочную информацию DWARF. Это связано с тем, что код, который подставляется для обеспечения поддержки perf, генерируется динамически и не содержит отладочной информации DWARF.
Проверить, была ли ваша система собрана с этим флагом, можно с помощью команды:
$ python -m sysconfig | grep 'no-omit-frame-pointer'
Если команда ничего не выводит, значит, ваш интерпретатор собран без указателей кадра и поэтому может не отображать функции Python в выводе perf.
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/howto/perf_profiling.html