2010-10-13 10 views
72

He estado usando cProfile para perfilar mi código, y ha estado funcionando muy bien. También uso gprof2dot.py para visualizar los resultados (lo hace un poco más claro).¿Cómo puedo crear un perfil de código python línea por línea?

Sin embargo, cProfile (y la mayoría de los demás perfiles de python que he visto hasta ahora) parecen tener solo un perfil en el nivel de llamada de función. Esto causa confusión cuando se llaman ciertas funciones desde diferentes lugares. No tengo idea si la llamada n. ° 1 o la n. ° 2 ocupan la mayor parte del tiempo. Esto empeora cuando la función en cuestión tiene 6 niveles de profundidad, llamada desde 7 otros lugares.

Entonces mi pregunta es: ¿cómo obtengo un perfil línea por línea? En lugar de esto:

function #12, total time: 2.0s 

Me gustaría ver algo como esto:

function #12 (called from somefile.py:102) 0.5s 
function #12 (called from main.py:12) 1.5s 

cprofile sí muestra cómo gran parte del tiempo total de "transferencias" a los padres, pero de nuevo se pierde esta conexión cuando tienes un montón de capas y llamadas interconectadas.

Idealmente, me encantaría tener una GUI que analizara los datos, luego muéstreme el archivo fuente con un tiempo total asignado a cada línea. Algo como esto:

main.py: 

a = 1 # 0.0s 
result = func(a) # 0.4s 
c = 1000 # 0.0s 
result = func(c) # 5.0s 

entonces yo sería capaz de hacer clic en el segundo "func (c)" llamado a ver lo que está tomando el tiempo en que la llamada, separado de la "func (a)" llamada.

¿Tiene sentido? ¿Hay alguna biblioteca de creación de perfiles que recopile este tipo de información? ¿Hay alguna herramienta increíble que me he perdido? Cualquier comentario es apreciado. ¡¡Gracias!!

+2

Mi conjetura es que usted estaría interesado en 'pstats.print_callers'. Un ejemplo es [aquí] (http://www.doughellmann.com/PyMOTW/profile/). –

+0

Muhammad, ¡eso definitivamente es útil! Al menos soluciona un problema: separar las llamadas de funciones según el origen. Creo que la respuesta de Joe Kington está más cerca de mi objetivo, pero print_callers() definitivamente me lleva a la mitad. ¡Gracias! – rocketmonkeys

Respuesta

84

Creo que para eso está destinado Robert Kern's line_profiler. Desde el enlace:

File: pystone.py 
Function: Proc2 at line 149 
Total time: 0.606656 s 

Line #  Hits   Time Per Hit % Time Line Contents 
============================================================== 
    149           @profile 
    150           def Proc2(IntParIO): 
    151  50000  82003  1.6  13.5  IntLoc = IntParIO + 10 
    152  50000  63162  1.3  10.4  while 1: 
    153  50000  69065  1.4  11.4   if Char1Glob == 'A': 
    154  50000  66354  1.3  10.9    IntLoc = IntLoc - 1 
    155  50000  67263  1.3  11.1    IntParIO = IntLoc - IntGlob 
    156  50000  65494  1.3  10.8    EnumLoc = Ident1 
    157  50000  68001  1.4  11.2   if EnumLoc == Ident1: 
    158  50000  63739  1.3  10.5    break 
    159  50000  61575  1.2  10.1  return IntParIO 

Espero que ayude!

+1

Joe, esto es * exactamente * lo que estaba buscando. Solo puedo usar un decorador, adjuntar un objeto LineProfiler() a algunas funciones, y escupirá un perfil línea por línea de la función. Realmente desearía que hubiera una forma gráfica de ver los resultados, ¡pero este es un gran comienzo! ¡Gracias! – rocketmonkeys

+1

Como seguimiento: lo he usado varias veces, e incluso he hecho un decorador django @profiler para ajustar automáticamente la vista en este generador de perfiles línea por línea y escupir los resultados. ¡Ha sido perfecto! Esto es realmente lo que se necesita cuando estoy perfilando una vista. No puede mostrarme recursivamente lo que lleva tiempo, pero al menos puedo reducirlo a una sola línea. Eso es a menudo justo lo que necesito. ¡Gracias de nuevo! – rocketmonkeys

+3

¿line_profiler funciona con Python 3? No pude obtener ninguna información sobre eso. – user1251007

19

También podría usar pprofile (pypi). Si desea perfilar toda la ejecución, no requiere modificación del código fuente. También puede perfilar un subconjunto de un programa mayor de dos maneras:

  • perfiles de palanca al llegar a un punto específico en el código, tales como:

    import pprofile 
    profiler = pprofile.Profile() 
    with profiler: 
        some_code 
    # Process profile content: generate a cachegrind file and send it to user. 
    
  • de palanca de perfiles de forma asíncrona de pila de llamadas (requiere una forma de activar este código en una aplicación considerada, por ejemplo, un manejador de señal o un hilo de trabajo disponible) mediante el uso de perfiles estadísticos:

    import pprofile 
    profiler = pprofile.StatisticalProfile() 
    statistical_profiler_thread = pprofile.StatisticalThread(
        profiler=profiler, 
    ) 
    with statistical_profiler_thread: 
        sleep(n) 
    # Likewise, process profile content 
    

formato de salida de anotación Código es muy similar a la línea de perfiles:

$ pprofile --threads 0 demo/threads.py 
Command line: ['demo/threads.py'] 
Total duration: 1.00573s 
File: demo/threads.py 
File duration: 1.00168s (99.60%) 
Line #|  Hits|   Time| Time per hit|  %|Source code 
------+----------+-------------+-------------+-------+----------- 
    1|   2| 3.21865e-05| 1.60933e-05| 0.00%|import threading 
    2|   1| 5.96046e-06| 5.96046e-06| 0.00%|import time 
    3|   0|   0|   0| 0.00%| 
    4|   2| 1.5974e-05| 7.98702e-06| 0.00%|def func(): 
    5|   1|  1.00111|  1.00111| 99.54%| time.sleep(1) 
    6|   0|   0|   0| 0.00%| 
    7|   2| 2.00272e-05| 1.00136e-05| 0.00%|def func2(): 
    8|   1| 1.69277e-05| 1.69277e-05| 0.00%| pass 
    9|   0|   0|   0| 0.00%| 
    10|   1| 1.81198e-05| 1.81198e-05| 0.00%|t1 = threading.Thread(target=func) 
(call)|   1| 0.000610828| 0.000610828| 0.06%|# /usr/lib/python2.7/threading.py:436 __init__ 
    11|   1| 1.52588e-05| 1.52588e-05| 0.00%|t2 = threading.Thread(target=func) 
(call)|   1| 0.000438929| 0.000438929| 0.04%|# /usr/lib/python2.7/threading.py:436 __init__ 
    12|   1| 4.79221e-05| 4.79221e-05| 0.00%|t1.start() 
(call)|   1| 0.000843048| 0.000843048| 0.08%|# /usr/lib/python2.7/threading.py:485 start 
    13|   1| 6.48499e-05| 6.48499e-05| 0.01%|t2.start() 
(call)|   1| 0.00115609| 0.00115609| 0.11%|# /usr/lib/python2.7/threading.py:485 start 
    14|   1| 0.000205994| 0.000205994| 0.02%|(func(), func2()) 
(call)|   1|  1.00112|  1.00112| 99.54%|# demo/threads.py:4 func 
(call)|   1| 3.09944e-05| 3.09944e-05| 0.00%|# demo/threads.py:7 func2 
    15|   1| 7.62939e-05| 7.62939e-05| 0.01%|t1.join() 
(call)|   1| 0.000423908| 0.000423908| 0.04%|# /usr/lib/python2.7/threading.py:653 join 
    16|   1| 5.26905e-05| 5.26905e-05| 0.01%|t2.join() 
(call)|   1| 0.000320196| 0.000320196| 0.03%|# /usr/lib/python2.7/threading.py:653 join 

Tenga en cuenta que debido a pProfile no se basa en la modificación del código que puede perfilar declaraciones del módulo de primer nivel, lo que permite al perfil tiempo de inicio del programa (el tiempo que lleva a importar módulos, inicializa globales, ...).

Puede generar salida con formato cachegrind, por lo que puede usar kcachegrind para navegar fácilmente por resultados grandes.

Divulgación: Soy autor de pprofile.

+0

+1 Gracias por su contribución. Se ve bien -done. Tengo una pequeña perspectiva diferente: medir el tiempo de inclusión tomado por las declaraciones y funciones es un objetivo. Descubrir qué se puede hacer para acelerar el código es un objetivo diferente. La diferencia se vuelve dolorosamente obvia a medida que el código se hace más grande, como 10^6 líneas de código. El código puede estar desperdiciando grandes porcentajes de tiempo. La forma en que lo encuentro es tomando un pequeño número de muestras muy detalladas y examinándolas con un ojo humano, sin resumir. El problema está expuesto por la fracción de tiempo que desperdicia. –

+1

Tiene razón, no mencioné el uso de perfil cuando se quiere perfilar un subconjunto más pequeño. Edité mi publicación para agregar ejemplos de esto. – vpelletier

+0

Esto es exactamente lo que estaba buscando: no intrusivo y extenso. – egpbos

0

PyVmMonitor tiene una vista en vivo que puede ayudarlo (puede conectarse a un programa en ejecución y obtener estadísticas de él).

Ver: http://www.pyvmmonitor.com/

Cuestiones relacionadas