2009-05-09 4 views
42

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

EDIT:

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

ответ

32

Это может не ответить на ваш вопрос напрямую, но определенно поможет. Если использовать профилировщик с опцией --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 в файл и используете модуль 'pstats' для загрузки профиля, вы можете напрямую запросить вызывающих абонентов тяжелой функции:' loaded_stats_object.print_callers ('heavy_function') ' –

1

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

+0

Ссылка больше не работает – industryworker3595112

94

Я почти всегда смотреть на выход модуля Cprofile с использованием Gprof2dot, в основном он преобразует выходной сигнал в виде GraphVis графа (а .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 
+2

как классный инструмент, придется попробовать это ;-) – ChristopheD

+2

+1 ЭТО ИМЕЕТ F ******** ВПЕЧАТЛЯЮ И УДИВИТЕЛЬНО! Спасибо, что показали это мне .. wow .. –

+0

Если вы хотите просмотреть выражение как с cProfile.run (exp), вы можете использовать это (все еще создает временный файл pstats): def run (exp, output = "profile.png", statsFileName = "статистика.pstats "): import cProfile cProfile.run (exp, statsFileName) os.system (" python gprof2dot.py -f pstats% s | dot -Tpng -o% s>/dev/null 2> & 1 "% (statsFileName, output)) – Ant6n

0

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

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

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

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

0

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

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

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

0

Это можно сделать, используя профилировщик 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. Хотя можно написать отдельный скрипт для этого)
  • Немного не связаны, но strip_dirs() должен пройти, прежде чем sort_stats() (в противном случае сортировка не работает)
+0

Это было упомянуто в одном из комментариев, но я думал, что это заслуживает отдельного ответа, потому что иногда внешние инструменты/зависимости невозможны . (хотя выход не так хорош, как в других ответах) – industryworker3595112

Смежные вопросы