Como posso fazer um perfil em Python linha a linha?

Tenho usado o perfil do meu código, e tem funcionado muito bem. Eu também uso gprof2dot.py para visualizar os resultados (torna-o um pouco mais claro).

No entanto, o perfil cProfile (e a maioria dos outros profilers de Python que já vi até agora) parece ter apenas perfil no nível de chamada de funções. Isto causa confusão quando certas funções são chamadas de diferentes lugares-eu não tenho idéia se call #1 ou call #2 está tomando a maioria do tempo. Isto fica ainda pior quando o a função em questão tem seis níveis de profundidade, chamados de outros sete lugares.

Como é que consigo um perfil Linha-A-linha?

em vez disto:

function #12, total time: 2.0s
Gostava de ver algo assim.
function #12 (called from somefile.py:102) 0.5s
function #12 (called from main.py:12) 1.5s

o CProfile mostra quanto do tempo total "transfers" para o pai, mas mais uma vez esta ligação perde-se quando se tem um monte de camadas e chamadas interligadas.

Idealmente, gostaria de ter uma interface gráfica que analisasse os dados e me mostrasse o meu ficheiro de código. com um tempo total dado a cada linha. Algo do género:
main.py:

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

Então eu seria capaz de clicar na segunda chamada" func(C)" para ver o que está tomando o tempo nessa chamada, separado da chamada "func (a)".

Isso faz sentido? Existe alguma biblioteca de perfis que recolha este tipo de informação? Perdi alguma ferramenta fantástica?

Author: Peter Mortensen, 2010-10-14

3 answers

Acho que é para isso que se destina o perfil de Line_profiler de Robert Kern. A partir da ligação:
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 isso ajude!
 97
Author: Joe Kington, 2010-10-13 20:19:45

Também pode utilizar pprofile(pypi ). Se você quiser traçar o perfil de toda a execução, ela não requer modificação de código fonte. Você também pode traçar um perfil de um subconjunto de um programa maior de duas maneiras:

  • Activa ou desactiva a análise ao atingir um ponto específico do Código, tais como:

    import pprofile
    profiler = pprofile.Profile()
    with profiler:
        some_code
    # Process profile content: generate a cachegrind file and send it to user.
    
  • Activa ou desactiva a análise assíncrona da pilha de chamadas (necessita de uma forma de activar este código na aplicação considerada, por exemplo, um manipulador de Sinais ou um (linha do trabalhador disponível) utilizando o perfil estatístico:

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

O formato de saída da anotação de código é muito parecido com o perfil de linha:

$ 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

Note que, como o pprofile não depende da modificação de código, pode analisar as declarações de Módulo de topo, permitindo traçar o tempo de arranque do programa (quanto tempo demora a importar módulos, inicializar globais, etc.)...).

Pode gerar uma saída formatada pelo cachegrind, por isso pode usar o kcachegrind para navegar por grandes resultados facilmente.

Disclosure: I am pprofile author.

 25
Author: vpelletier, 2015-02-03 08:23:26

O PyVmMonitor tem uma vista ao vivo que o pode ajudar (pode ligar-se a um programa em execução e obter estatísticas a partir dele).

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

 1
Author: Fabio Zadrozny, 2015-04-28 23:12:07