Подтвердить что ты не робот

Профилирование в Python: кто вызвал функцию?

Я профилирую Python, используя cProfile. Я нашел функцию, которая занимает много процессорного времени. Как узнать, какая функция больше всего называет эту тяжелую функцию?

EDIT:

Я соглашусь на обходной способ: могу ли я написать строку Python внутри этой тяжелой функции, которая будет печатать имя вызываемой функции?

4b9b3361

Ответ 1

Это может не ответить на ваш вопрос напрямую, но определенно поможет. Если использовать профилировщик с опцией --sort кумулятивный, он будет сортировать функции по кумулятивному времени. Это полезно для обнаружения не только тяжелых функций, но и функций, которые их называют.

python -m cProfile --sort cumulative myScript.py

Существует обходное решение для получения функции звонящего:

import inspect
print inspect.getframeinfo(inspect.currentframe().f_back)[2]

Вы можете добавить столько f_back, сколько хотите, если вы хотите, чтобы вызывающий вызывающий и т.д. Если вы хотите рассчитать частые звонки, вы можете сделать это:

record = {}

caller = inspect.getframeinfo(inspect.currentframe().f_back)[2]
record[caller] = record.get(caller, 0) + 1

Затем напечатайте их по порядку частоты:

print sorted(record.items(), key=lambda a: a[1])

Ответ 2

Я почти всегда просматриваю вывод модуля cProfile с помощью Gprof2dot, в основном он преобразует вывод в график графика (a .dot файл), например:

example gprof2dot output

Это очень легко определить, какая функция самая медленная, и какую функцию [s] называют ее.

Использование:

python -m cProfile -o output.pstats path/to/your/script arg1 arg2
gprof2dot.py -f pstats output.pstats | dot -Tpng -o output.png

Ответ 3

inspect.stack() даст вам текущий стек вызывающего абонента.

Ответ 4

Возможно, вы захотите взглянуть на pycallgraph.

Ответ 5

Я сам не использовал cProfile, но большинство профилировщиков предоставляют вам иерархию вызовов.
Googling Я нашел этот слайды о cProfile. Может быть, это помогает. Page 6 выглядит так: cProfile предоставляет иерархию.

Ответ 6

Извините, я не знаком с Python, но там общий метод работает, если вы можете вручную прерывать выполнение в произвольное время.

Просто сделайте это и отобразите стек вызовов. Он расскажет вам, с большой вероятностью, то, что вы хотите знать. Если вы хотите быть более уверенным, просто делайте это несколько раз.

Это работает, потому что виновный вызывающий должен находиться в стеке вызовов за долю времени, потраченного впустую, что в течение этого времени предоставляет его прерываниям, распространяется ли оно на многие короткие вызовы или на несколько длинных из них.

ПРИМЕЧАНИЕ. Этот процесс больше похож на диагностику, чем на измерение. Предположим, что плохой вызов тратит 90% времени. Затем каждый раз, когда вы его останавливаете, вероятность 90% заключается в том, что оператор плохого вызова находится прямо в стеке вызовов, чтобы вы могли видеть, и вы увидите, что это плохо. Однако, если вы хотите точно измерить потерю, это другая проблема. Для этого вам понадобится намного больше образцов, чтобы узнать, в каком% из них содержится этот вызов. Или, альтернативно, просто исправьте виновный звонок, отметьте ускорение, и это точно скажет вам, что произошло.

Ответ 7

Pycscope делает это. Я просто нашел его сегодня, поэтому я не могу говорить о том, насколько он хорош, но несколько примеров, которые я пробовал, были довольно хорошими (хотя и не идеальными).

https://pypi.python.org/pypi/pycscope/

Вы использовали бы это для создания файла cscope, а затем плагина cscope из редактора VIM. Я попытался использовать его с ванильным cscope, кажется, что простой cscope запутался.

Ответ 8

Это можно сделать с помощью профайлера cProfile в стандартной библиотеке.
В pstats.Stats (результат профилирования) существует метод print_callees (или, альтернативно, print_callers).


Пример кода:

import cProfile, pstats
pr = cProfile.Profile()
pr.enable()

# ... do something ...

pr.disable()
ps = pstats.Stats(pr).strip_dirs().sort_stats('cumulative')
ps.print_callees()

Результат будет выглядеть примерно так:

Function                           called...
                                       ncalls  tottime  cumtime
ElementTree.py:1517(_start_list)   ->   24093    0.048    0.124  ElementTree.py:1399(start)
                                        46429    0.015    0.041  ElementTree.py:1490(_fixtext)
                                        70522    0.015    0.015  ElementTree.py:1497(_fixname)
ElementTree.py:1527(_data)         ->   47827    0.017    0.026  ElementTree.py:1388(data)
                                        47827    0.018    0.053  ElementTree.py:1490(_fixtext)

Слева у вас есть вызывающий, справа у вас есть вызывающий.
(например, _fixtext вызывается из _data 47827 раз и от _start_list 46429 раз)


См. также:


Пара примечаний:

  • Для этого вам необходимо отредактировать код (вставьте эти утверждения профиля).
    (т.е. невозможно использовать из командной строки, например python -m cProfile myscript.py. Хотя для этого можно написать отдельный script)
  • Немного несвязанный, но strip_dirs() должен идти до sort_stats() (иначе сортировка не работает)