Инстанцирование классов профилирования строк в Python - PullRequest
2 голосов
/ 19 марта 2019

У меня есть существующий код, который я пытаюсь профилировать. Я могу успешно выстроить методы класса профиля, добавив @profile декоратор, используя kernprof .

Существует ли общий способ профилирования классов? У меня есть несколько классов, которые имеют довольно сложную структуру наследования. Когда я пытаюсь профилировать их функции инициализации, я получаю нечто подобное:

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   179                                               def __init__(self, data):
   180         1    8910739.0 8910739.0    100.0          super().__init__(data)
   181         1         10.0      10.0      0.0          self.mortgage_rate = 5.2  # rate in percentage

Это немного бесполезно, потому что я не знаю, какая фактическая родительская функция __init__ (у этого класса есть 2 родителя, у каждого из которых есть один или несколько родителей).

Есть ли способ сделать это лучше? Например, есть ли способ автоматически погрузиться в каждую строку и профилировать строки, которые ей вызываются (с ограниченной глубиной)?

1 Ответ

3 голосов
/ 24 марта 2019

Существует много способов:

Модуль trace

Модуль trace в стандартных библиотеках python предоставляет удобную функцию для отслеживания выполнения вашей программы., построчно.Поэтому довольно легко определить, какая функция вызывается вашим __init__ методом.

Попробуйте запустить следующие коды в оболочке python

from MyMod import MyClass
# Do necessary preparation for your module HERE

# --- Setup and start tracing ---
import sys, trace
tracer = trace.Trace( trace=0, count=0, timing=True,  countcallers=True)
tracer.run('MyClass()') # init your class and track the function calls
tracer.results().write_results(show_missing=False) # print result to the screen

Трассер отобразит отношения вызовавыставляется при запуске программы.

MyDependency.Fourth.__init__ -> MyDependency.Second.__init__
MyDependency.Second.__init__ -> MyDependency.Third.__init__
MyDependency.Third.__init__ -> MyDependency.First.__init__
MyClass.Child.__init__ -> MyDependency.Fourth.__init__

Модуль trace также имеет интерфейс командной строки.Вышеуказанные коды Python эквивалентны этой команде оболочки:

python -m trace -T test.py | grep __init__

, где опция -T эквивалентна countcallers=True.Целевой сценарий test.py должен содержать минимальные коды для инициализации вашего класса.

Добавить строковый профилировщик к вызываемым функциям

Теперь вы знаете имена модулей, классов и методов, которые были вызваны винициализация вашего класса.Затем вы можете добавить @profile декоратор к этим функциям.В качестве примечания: нет необходимости изменять исходный код каждого модуля для добавления декоратора.Просто импортируйте их в свой основной модуль и запустите profile.add_function(MyDependency.Third.__init__), чтобы получить тот же эффект.

Если вы хотите получить хронологическую трассировку всех строк кода Python, которые были вызваны, используйте следующие опции

tracer = trace.Trace( ignoredirs=[sys.prefix, sys.exec_prefix ], trace=1, count=0, timing=True )

Будет распечатано

 --- modulename: MyMod, funcname: __init__
0.00 MyMod.py(6):         super().__init__()
 --- modulename: MyDependency, funcname: __init__
0.00 MyDependency.py(17):         super().__init__()
...

, где первый столбец - время часов ходьбы.

Метод sys.setprofile

Вы можете зарегистрировать обратный вызовфункция с помощью метода sys.setprofile.Он будет получать события перехода в стек (когда функция вызывается или возвращается).Каждое событие поставляется с объектом стекового фрейма, из которого вы можете записать вызываемый модуль, класс и функцию.

Этот метод даст вам максимальную гибкость.Например, вы можете отфильтровать вызовы функций по глубине стека или продолжительности выполнения.Пример использования см. В моем старом посте для аналогичного вопроса.

Структура файла для приведенных выше примеров

Приведенные выше результаты основаны на следующем модуле / классеструктура взята из другой пост .

Файл "MyDependency.py"

class First:
    ...
class Second(First):
    ...
class Third(First):
    ...
class Fourth(Second, Third):
    ...

Файл "MyModel.py"

from MyDependency import Fourth
class MyClass(Fourth):
    def __init__(self):
        super().__init__()
...