Spec-Zone.ru › Python 3.12

Поддержка Python для профилировщика perf Linux

author:

Пабло Галиндо

Профилировщик perf Linux — очень мощный инструмент, позволяющий профилировать и получать информацию о производительности вашего приложения. perf также обладает очень богатым экосистемой инструментов, которые помогают анализировать данные, которые он генерирует.

Основная проблема использования профилировщика perf с приложениями Python заключается в том, что perf получает информацию только о нативных символах, то есть о названиях функций и процедур, написанных на C. Это означает, что имена и имена файлов функций Python в вашем коде не будут отображаться в выходных данных perf.

Начиная с Python 3.12, интерпретатор может работать в специальном режиме, который позволяет отображать функции Python в выходных данных профилировщика perf. При включении этого режима интерпретатор вставит небольшой фрагмент кода, скомпилированный на лету, перед выполнением каждой функции Python, и он научит perf соотношению между этим фрагментом кода и соответствующей функцией Python, используя файлы perf map.

Примечание

Поддержка профилировщика 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API