2009-05-09 11 views
42

Estoy perfilando en Python usando cProfile. Encontré una función que requiere mucho tiempo de CPU. ¿Cómo puedo saber qué función llama más a esta función pesada?Perfilado en Python: ¿Quién llamó a la función?

EDIT:

me conformo con una solución: ¿Puedo escribir una línea de Python dentro de esa función pesada que imprimirá el nombre de la función que la llamó?

Respuesta

32

Eso puede no responder a su pregunta directamente, pero definitivamente ayudará. Si usa el generador de perfiles con la opción - ordenado acumulativo, ordenará las funciones por tiempo acumulado. Lo cual es útil para detectar no solo las funciones pesadas sino también las funciones que las llaman.

python -m cProfile --sort cumulative myScript.py 

Hay una solución para obtener la función de llamada:

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

Puede añadir tantos f_back como desee en caso de que desee la persona que llama que llama etc Si desea calcular frecuentes te llama puede hacer esto:

record = {} 

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

Entonces imprimirlos por orden de frecuencia:

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

Si guarda los resultados de cProfile en un archivo y utiliza el módulo 'pstats' para cargar el perfil, puede consultar directamente las llamadas de la función pesada:' loaded_stats_object.print_callers ('heavy_function') ' –

1

No he usado cProfile, pero la mayoría de los profilers le dan una jerarquía de llamadas.
Googling Encontré este slides sobre cProfile. Tal vez eso ayude. Parece que cProfile proporciona una jerarquía.

+0

El enlace ya no funciona – industryworker3595112

94

casi siempre ver la salida del módulo cprofile usando Gprof2dot, básicamente, que convierte la salida en un gráfico GraphVis (un archivo .dot), por ejemplo:

example gprof2dot output

Esto hace que sea muy fácil determinar qué función es la más lenta, y qué función [s] la llamó.

uso es:

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

Parece como una herramienta genial, tendrá que probar esto ;-) – ChristopheD

+2

+1 ESTO ES F ******** ¡IMPRESIONANTE E INCREÍBLE! Gracias por mostrarme esto wow .. –

+0

Si desea crear un perfil de una expresión como con cProfile.run (exp), puede usar esto (aún crea un archivo de pstats temporal): def run (exp, output = "profile.png", statsFileName = "stats.pstats "): import cProfile cProfile.run (exp, statsFileName) os.system (" python gprof2dot.py -f pstats% s | dot-Tp -o% s>/dev/null 2> & 1 "% (statsFileName, salida)) – Ant6n

0

Lo siento, no estoy familiarizado con Python, pero hay una general method que funciona, suponiendo que se puede interrumpir manualmente la ejecución en un tiempo aleatorio.

Simplemente hágalo y visualice la pila de llamadas. Te dirá, con alta probabilidad, lo que quieres saber. Si quieres estar más seguro, hazlo varias veces.

Funciona porque la persona que llama culpable tiene que estar en la pila de llamadas durante la fracción de tiempo desperdiciada, lo que la expone a las interrupciones durante la mayor parte del tiempo, ya sea durante llamadas cortas o algunas Largos.

NOTA: Este proceso es más parecido al diagnóstico que a la medición. Supongamos que una mala llamada está perdiendo el 90% del tiempo. Luego, cada vez que la detenga, la probabilidad es del 90% de que la declaración de llamada incorrecta esté allí en la pila de llamadas para que usted la vea, y podrá ver que es mala.Sin embargo, si quiere medir exactamente el desperdicio, ese es un problema diferente. Para eso, necesitará muchas más muestras, para ver qué porcentaje contiene esa llamada. O, como alternativa, solo arregla la llamada culpable, registra la aceleración, y eso te dirá exactamente qué fue el desperdicio.

0

Pycscope hace esto. Lo acabo de encontrar hoy, así que no puedo decir lo bueno que es, pero los pocos ejemplos que he probado han sido bastante buenos (aunque no perfectos).

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

Se podría usar esto para generar un archivo cscope y luego un plugin cscope de un editor, VIM específicamente. Intenté usarlo con cscope de vainilla, parece que el cscope simple se confunde.

0

Es posible hacerlo utilizando Profiler cProfile en la biblioteca estándar.
En pstats.Stats (el resultado del perfilador) existe el método print_callees (o alternativamente print_callers).


código Ejemplo:

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

# ... do something ... 

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

El resultado será algo como:

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) 

A la izquierda tiene la persona que llama, a la derecha tienes el destinatario de la llamada.
(por ejemplo _fixtext fue llamado desde _data 47827 veces y de _start_list 46429 veces)


Consulte también:


par de notas:

  • el código necesita ser editado para este (insertar dichos estados perfil).
    (es decir, no es posible utilizar desde la línea de comandos como python -m cProfile myscript.py. Aunque es posible escribir la escritura separada para eso)
  • Un poco sin relación, pero strip_dirs() debe recorrer antes de sort_stats() (lo contrario de clasificación no funciona)
+0

Esto fue mencionado en uno de los comentarios, pero pensé que merecía ser una respuesta separada, porque a veces las herramientas/dependencias externas no son posibles . (a pesar de que la salida no es tan buena, como en otras respuestas) – industryworker3595112

Cuestiones relacionadas