Como você pode criar um perfil de um script Python?

1283

O Project Euler e outros concursos de codificação geralmente têm um tempo máximo para execução ou as pessoas se gabam da rapidez com que sua solução específica é executada. Com o Python, às vezes as abordagens são um pouco esquisitas - ou seja, adicionando código de temporização ao __main__.

Qual é uma boa maneira de criar um perfil de quanto tempo um programa Python leva para executar?

Chris Lawlor
fonte
113
Os programas do euler do projeto não devem precisar de criação de perfil. Você possui um algoritmo que funciona em menos de um minuto ou o algoritmo totalmente errado. "Ajustar" raramente é apropriado. Você geralmente precisa adotar uma nova abordagem.
245/09 S.Lott
105
S.Lott: a criação de perfil geralmente é uma maneira útil de determinar quais sub-rotinas são lentas. Sub-rotinas que demoram muito tempo são ótimas candidatas à melhoria algorítmica.
stalepretzel

Respostas:

1369

Python inclui um criador de perfil chamado cProfile . Ele não apenas fornece o tempo total de execução, mas também cronometra cada função separadamente e informa quantas vezes cada função foi chamada, facilitando a determinação de onde você deve fazer otimizações.

Você pode chamá-lo de dentro do seu código ou do intérprete, assim:

import cProfile
cProfile.run('foo()')

Ainda mais útil, você pode chamar o cProfile ao executar um script:

python -m cProfile myscript.py

Para tornar ainda mais fácil, criei um pequeno arquivo em lote chamado 'profile.bat':

python -m cProfile %1

Então, tudo o que tenho a fazer é executar:

profile euler048.py

E eu entendo isso:

1007 function calls in 0.061 CPU seconds

Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.061    0.061 <string>:1(<module>)
 1000    0.051    0.000    0.051    0.000 euler048.py:2(<lambda>)
    1    0.005    0.005    0.061    0.061 euler048.py:2(<module>)
    1    0.000    0.000    0.061    0.061 {execfile}
    1    0.002    0.002    0.053    0.053 {map}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler objects}
    1    0.000    0.000    0.000    0.000 {range}
    1    0.003    0.003    0.003    0.003 {sum}

EDIT: Link atualizado para um bom recurso de vídeo do PyCon 2013 intitulado Python Profiling
Also via YouTube .

Chris Lawlor
fonte
251
Também é útil classificar os resultados, que podem ser feitos pela opção -s, por exemplo: '-s time'. Você pode usar as opções de classificação cumulativa / nome / hora / arquivo.
25410 Jiri
19
Também é importante notar que você pode usar o módulo cProfile do ipython usando a função mágica% prun (execução de perfil). Primeiro importe seu módulo e, em seguida, chame a função principal com% prun: import euler048; % prun euler048.main ()
RussellStewart
53
Para visualizar os dumps do cProfile (criados por python -m cProfile -o <out.profile> <script>), RunSnakeRun , chamado como runsnake <out.profile>inestimável.
Lily Chung
13
@NeilG mesmo para python 3, cprofileainda é recomendado mais profile.
Trichoplax
17
Para visualizar os dumps do cProfile, o RunSnakeRun não é atualizado desde 2011 e não suporta python3. Em vez disso, você deve usar snakeviz
Giacomo Tecya Pigani -
424

Há um tempo atrás eu fiz o pycallgraphque gera uma visualização do seu código Python. Edit: Eu atualizei o exemplo para trabalhar com o 3.3, a versão mais recente deste artigo.

Após a pip install pycallgraphinstalação do GraphViz, você pode executá-lo na linha de comando:

pycallgraph graphviz -- ./mypythonscript.py

Ou você pode criar um perfil de partes específicas do seu código:

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

with PyCallGraph(output=GraphvizOutput()):
    code_to_profile()

Qualquer um desses itens gerará um pycallgraph.pngarquivo semelhante à imagem abaixo:

insira a descrição da imagem aqui

gak
fonte
43
Você está colorindo com base na quantidade de chamadas? Nesse caso, você deve colorir com base no tempo, porque a função com mais chamadas nem sempre é a que leva mais tempo.
vermelho
21
@red Você pode personalizar as cores como quiser e até de forma independente para cada medição. Por exemplo, vermelho para chamadas, azul para o tempo, verde para uso da memória.
gak
2
recebendo este erroTraceback (most recent call last): /pycallgraph.py", line 90, in generate output.done() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 94, in done source = self.generate() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 143, in generate indent_join.join(self.generate_attributes()), File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 169, in generate_attributes section, self.attrs_from_dict(attrs), ValueError: zero length field name in format
ciasto Piekarz
3
Eu atualizei isso para mencionar que você precisa instalar o GraphViz para que as coisas funcionem conforme descrito. No Ubuntu, isso é apenas sudo apt-get install graphviz.
mlissner
2
Isso requer um pouco de trabalho para instalar aqui. São 3 etapas para ajudar. 1. Instale via pip, 2. Instale o GraphViz via exe 3. Configure as variáveis ​​de caminho para o diretório GraphViz 4. Descubra como corrigir todos os outros erros. 5. Descobrir onde ele salva o arquivo png?
pântano
199

Vale ressaltar que o uso do criador de perfil só funciona (por padrão) no thread principal e você não obterá nenhuma informação de outros threads se usá-los. Isso pode ser um pouco complicado, pois não é mencionado na documentação do criador de perfil .

Se você também deseja criar um perfil de threads, consulte a threading.setprofile()função nos documentos.

Você também pode criar sua própria threading.Threadsubclasse para fazer isso:

class ProfiledThread(threading.Thread):
    # Overrides threading.Thread.run()
    def run(self):
        profiler = cProfile.Profile()
        try:
            return profiler.runcall(threading.Thread.run, self)
        finally:
            profiler.dump_stats('myprofile-%d.profile' % (self.ident,))

e use essa ProfiledThreadclasse em vez da classe padrão. Pode oferecer mais flexibilidade, mas não tenho certeza se vale a pena, especialmente se você estiver usando código de terceiros que não usaria sua classe.

Joe Shaw
fonte
1
Também não vejo nenhuma referência ao runcall na documentação. Dando uma olhada no cProfile.py, não sei por que você usa a função threading.Thread.run nem self como argumento. Eu esperava ver uma referência ao método de execução de outro thread aqui.
PypeBros
Não está na documentação, mas está no módulo. Consulte hg.python.org/cpython/file/6bf07db23445/Lib/cProfile.py#l140 . Isso permite criar um perfil de uma chamada de função específica e, no nosso caso, queremos criar um perfil da targetfunção do Thread , que é o que a threading.Thread.run()chamada executa. Mas, como eu disse na resposta, provavelmente não vale a pena subclassificar o Thread, pois qualquer código de terceiros não o utilizará e, em vez disso, o utilizará threading.setprofile().
Joe Shaw
9
agrupar o código com profiler.enable () e profiler.disable () também parece funcionar muito bem. Isso é basicamente o que o runcall faz e não impõe nenhum número de argumentos ou coisas semelhantes.
PypeBros
1
Eu combinei meu próprio stackoverflow.com/questions/10748118/... com ddaa.net/blog/python/lsprof-calltree e kindof obras; -)
Dima Tisnek
1
Joe, você sabe como o criador de perfil brinca com asyncio no Python 3.4?
Nick Chammas
149

O wiki do python é uma ótima página para recursos de criação de perfil: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

assim como os documentos python: http://docs.python.org/library/profile.html

como mostrado por Chris Lawlor cProfile é uma ótima ferramenta e pode ser facilmente usada para imprimir na tela:

python -m cProfile -s time mine.py <args>

ou arquivar:

python -m cProfile -o output.file mine.py <args>

PS> Se você estiver usando o Ubuntu, instale o python-profile

apt-get install python-profiler 

Se você imprimir em arquivo, poderá obter boas visualizações usando as seguintes ferramentas

PyCallGraph: uma ferramenta para criar imagens de gráficos de chamadas
:

 pip install pycallgraph

corre:

 pycallgraph mine.py args

Visão:

 gimp pycallgraph.png

Você pode usar o que quiser para ver o arquivo png, usei o gimp
Infelizmente, eu sempre

ponto: o gráfico é muito grande para os bitmaps do cairo-renderer. Dimensionamento de 0,257079 para ajustar

o que torna minhas imagens inutilmente pequenas. Então, geralmente crio arquivos svg:

pycallgraph -f svg -o pycallgraph.svg mine.py <args>

PS> certifique-se de instalar o graphviz (que fornece o programa de pontos):

pip install graphviz

Gráfico alternativo usando gprof2dot via @maxy / @quodlibetor:

pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
brent.payne
fonte
12
O gprof2dot também pode fazer esses gráficos. Eu acho que a saída é um pouco melhor ( exemplo ).
Max13
2
graphviz também é necessária se você estiver usando OSX
Vaibhav Mishra
134

O comentário de Maxy sobre essa resposta me ajudou o suficiente para que eu mereça a sua própria resposta: eu já tinha arquivos .pstats gerados pelo cProfile e não queria executar novamente as coisas com o pycallgraph, então usei o gprof2dot e fiquei bonita svgs:

$ sudo apt-get install graphviz
$ git clone https://github.com/jrfonseca/gprof2dot
$ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin
$ cd $PROJECT_DIR
$ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg

e BLAM!

Ele usa ponto (a mesma coisa que o pycallgraph usa) para que a saída seja semelhante. Tenho a impressão de que o gprof2dot perde menos informações:

exemplo de saída gprof2dot

quodlibetor
fonte
1
Boa abordagem, funciona muito bem, pois você pode visualizar o SVG no Chrome etc. e escalá-lo para cima / baixo. A terceira linha tem erro de digitação, deve ser: ln -s pwd/gprof2dot/gprof2dot.py $ HOME / bin (ou use ln -s $ PWD / gprof2dot / gprof2dot.py ~ / bin na maioria dos shells - o sotaque grave é usado como formatação no primeiro versão).
precisa saber é o seguinte
2
Ah, bom argumento. Entendo lnerrado a ordem dos argumentos quase sempre.
quodlibetor
7
o truque é lembrar que ln e cp têm a mesma ordem de argumento - pense nisso como 'copiando arquivo1 para arquivo2 ou dir2, mas criando um link'
RichVel
Isso faz sentido, acho que o uso de "TARGET" na página de manual me impressiona.
quodlibetor
Por favor, como você conseguiu cantos arredondados? Eu sinto que melhora a legibilidade. Acabei de receber cantos afiados e feios que não são legais na presença de muitas arestas em volta das caixas.
precisa saber é o seguinte
78

Encontrei uma ferramenta útil chamada SnakeViz ao pesquisar este tópico. O SnakeViz é uma ferramenta de visualização de perfis baseada na Web. É muito fácil de instalar e usar. A maneira usual de usá-lo é gerar um arquivo stat %prune depois fazer a análise no SnakeViz.

A principal técnica de visualização usada é o gráfico Sunburst, como mostrado abaixo, no qual a hierarquia das chamadas de função é organizada como camadas de arcos e informações de tempo codificadas em suas larguras angulares.

A melhor coisa é que você pode interagir com o gráfico. Por exemplo, para ampliar, é possível clicar em um arco, e o arco e seus descendentes serão ampliados como um novo raio de sol para exibir mais detalhes.

insira a descrição da imagem aqui

zaxliu
fonte
1
A resposta do CodeCabbie inclui as instruções (curtas) de instalação e mostra como (facilmente) usar o SnakeViz.
Oren Milman
Aqui eu li guia boa IMHO como usar perfil para Python no caderno jupyter: towardsdatascience.com/speed-up-jupyter-notebooks-20716cbe2025
Alexei Martianov
73

A maneira mais simples e rápida de descobrir para onde está indo o tempo todo.

1. pip install snakeviz

2. python -m cProfile -o temp.dat <PROGRAM>.py

3. snakeviz temp.dat

Desenha um gráfico de pizza em um navegador. A maior peça é a função do problema. Muito simples.

CodeCabbie
fonte
1
Isso foi muito útil. Obrigado.
jippyjoe4 17/02
55

Eu acho que isso cProfileé ótimo para criação de perfil, enquanto kcachegrindé ótimo para visualizar os resultados. O pyprof2calltreemeio lida com a conversão do arquivo.

python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree

Para instalar as ferramentas necessárias (no Ubuntu, pelo menos):

apt-get install kcachegrind
pip install pyprof2calltree

O resultado:

Captura de tela do resultado

Federico
fonte
9
Usuários de Mac instalar brew install qcachegrinde substitude cada kcachegrind com qcachegrind a descrição de perfis de sucesso.
precisa
Eu tive que fazer isso para fazê-lo funcionar:export QT_X11_NO_MITSHM=1
Yonatan Simson
41

Também vale mencionar o visualizador de despejo de GUI cProfile RunSnakeRun . Ele permite que você ordene e selecione, ampliando assim as partes relevantes do programa. O tamanho dos retângulos na imagem é proporcional ao tempo gasto. Se você passar o mouse sobre um retângulo, destacará as chamadas na tabela e em todos os lugares do mapa. Quando você clica duas vezes em um retângulo, ele aumenta o zoom nessa parte. Ele mostrará quem chama essa parte e o que essa parte chama.

A informação descritiva é muito útil. Ele mostra o código desse bit que pode ser útil quando você está lidando com chamadas de biblioteca internas. Ele informa qual arquivo e qual linha encontrar o código.

Também quero destacar que o OP disse 'criação de perfil', mas parece que ele quis dizer 'tempo'. Lembre-se de que os programas serão mais lentos quando analisados.

insira a descrição da imagem aqui

Pete
fonte
34

Um bom módulo de criação de perfil é o line_profiler (chamado usando o script kernprof.py). Pode ser baixado aqui .

Meu entendimento é que o cProfile fornece apenas informações sobre o tempo total gasto em cada função. Portanto, linhas de código individuais não são cronometradas. Esse é um problema na computação científica, pois muitas vezes uma única linha pode levar muito tempo. Além disso, como me lembro, o cProfile não pegou o tempo que eu estava gastando em digitar numpy.dot.

Ian Langmore
fonte
34

Recentemente, criei atum para visualizar perfis de tempo de execução e importação do Python; isso pode ser útil aqui.

insira a descrição da imagem aqui

Instale com

pip install tuna

Crie um perfil de tempo de execução

python3 -m cProfile -o program.prof yourfile.py

ou um perfil de importação (é necessário o Python 3.7+)

python3 -X importprofile yourfile.py 2> import.log

Em seguida, basta executar o atum no arquivo

tuna program.prof
Nico Schlömer
fonte
33

pprofile

line_profiler(já apresentado aqui) também inspirado pprofile, descrito como:

Perfil de granularidade de linha, determinístico e estatístico com reconhecimento de segmento puro-python

Ele fornece granularidade de linha, pois line_profileré um Python puro, pode ser usado como um comando ou módulo independente e pode até gerar arquivos no formato de chamada de grupo que podem ser facilmente analisados [k|q]cachegrind.

vprof

Há também o vprof , um pacote Python descrito como:

fornecendo visualizações ricas e interativas para várias características do programa Python, como tempo de execução e uso de memória.

mapa de calor

BenC
fonte
14

Há muitas ótimas respostas, mas elas usam a linha de comando ou algum programa externo para criar um perfil e / ou classificar os resultados.

Eu realmente senti falta de alguma maneira que eu poderia usar no meu IDE (eclipse-PyDev) sem tocar na linha de comando ou instalar nada. Então aqui está.

Criação de perfil sem linha de comando

def count():
    from math import sqrt
    for x in range(10**5):
        sqrt(x)

if __name__ == '__main__':
    import cProfile, pstats
    cProfile.run("count()", "{}.profile".format(__file__))
    s = pstats.Stats("{}.profile".format(__file__))
    s.strip_dirs()
    s.sort_stats("time").print_stats(10)

Consulte documentos ou outras respostas para obter mais informações.

David Mašek
fonte
por exemplo, o perfil imprime {map} ou {xxx}. como sei que o método {xxx} é chamado de qual arquivo? meu perfil imprime {método 'compactar' de 'objetos zlib.Compress'} leva a maior parte do tempo, mas eu não uso nenhum zlib, então acho que alguma função de chamada numpy pode usá-lo. Como sei qual é exatamente o arquivo e a linha que leva muito tempo?
machen
12

Seguindo a resposta de Joe Shaw sobre o código multiencadeado para não funcionar conforme o esperado, runcallconcluí que o método no cProfile está apenas executando self.enable()e self.disable()chama a chamada de função com perfil, para que você possa fazer isso sozinho e ter o código que desejar entre eles. interferência mínima com o código existente.

PypeBros
fonte
3
Excelente dica! Uma rápida olhada no cprofile.pycódigo-fonte do revela que é exatamenteruncall() isso que faz. Para ser mais específico, depois de criar uma instância de Perfil com prof = cprofile.Profile(), chame imediatamente prof.disable()e adicione prof.enable()e prof.disable()chame a seção de código que você deseja criar.
21412 martineau
Isso é muito útil, mas parece que o código que está realmente entre habilitar e desabilitar não tem perfil - apenas as funções que chama. Eu tenho esse direito? Eu teria que agrupar esse código em uma chamada de função para contar para qualquer um dos números em print_stats ().
9788 Bob Stein
10

Na fonte de Virtaal, há uma classe e um decorador muito úteis que podem facilitar muito a criação de perfis (mesmo para métodos / funções específicos). A saída pode ser visualizada com muito conforto no KCacheGrind.

Walter
fonte
1
Obrigado por esta jóia. FYI: Isso pode ser usado como um módulo autônomo com qualquer código, base de código Virtaal não é necessária. Apenas salve o arquivo em profiling.py e importe o profile_func (). Use @profile_func () como decorador para qualquer função que você precise criar perfil e viola. :)
Amjith
9

O cProfile é ótimo para criação de perfil rápido, mas na maioria das vezes era finalizado para mim com os erros. A função runctx resolve esse problema inicializando corretamente o ambiente e as variáveis, espero que possa ser útil para alguém:

import cProfile
cProfile.runctx('foo()', None, locals())
Datageek
fonte
7

Se você deseja criar um criador de perfil cumulativo, ou seja, executar a função várias vezes seguidas e observar a soma dos resultados.

você pode usar este cumulative_profilerdecorador:

é python> = 3.6 específico, mas você pode remover nonlocalpara que funcione em versões mais antigas.

import cProfile, pstats

class _ProfileFunc:
    def __init__(self, func, sort_stats_by):
        self.func =  func
        self.profile_runs = []
        self.sort_stats_by = sort_stats_by

    def __call__(self, *args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()  # this is the profiling section
        retval = self.func(*args, **kwargs)
        pr.disable()

        self.profile_runs.append(pr)
        ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
        return retval, ps

def cumulative_profiler(amount_of_times, sort_stats_by='time'):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            nonlocal function, amount_of_times, sort_stats_by  # for python 2.x remove this row

            profiled_func = _ProfileFunc(function, sort_stats_by)
            for i in range(amount_of_times):
                retval, ps = profiled_func(*args, **kwargs)
            ps.print_stats()
            return retval  # returns the results of the function
        return wrapper

    if callable(amount_of_times):  # incase you don't want to specify the amount of times
        func = amount_of_times  # amount_of_times is the function in here
        amount_of_times = 5  # the default amount
        return real_decorator(func)
    return real_decorator

Exemplo

perfilando a função baz

import time

@cumulative_profiler
def baz():
    time.sleep(1)
    time.sleep(2)
    return 1

baz()

baz correu 5 vezes e imprimiu isto:

         20 function calls in 15.003 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   15.003    1.500   15.003    1.500 {built-in method time.sleep}
        5    0.000    0.000   15.003    3.001 <ipython-input-9-c89afe010372>:3(baz)
        5    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

especificando a quantidade de vezes

@cumulative_profiler(3)
def baz():
    ...
moshevi
fonte
7

A solução apenas para terminal (e mais simples), no caso de todas essas UI sofisticadas falharem na instalação ou na execução:
ignore cProfilecompletamente e substitua-a por pyinstrument, que coletará e exibirá a árvore de chamadas logo após a execução.

Instalar:

$ pip install pyinstrument

Perfil e resultado da exibição:

$ python -m pyinstrument ./prog.py

Funciona com python2 e 3.

[EDIT] A documentação da API, para criação de perfil apenas uma parte do código, pode ser encontrada aqui .

François
fonte
6

Meu jeito é usar o yappi ( https://github.com/sumerc/yappi ). É especialmente útil combinado com um servidor RPC onde (mesmo apenas para depuração) você registra o método para iniciar, parar e imprimir informações de criação de perfil, por exemplo:

@staticmethod
def startProfiler():
    yappi.start()

@staticmethod
def stopProfiler():
    yappi.stop()

@staticmethod
def printProfiler():
    stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20)
    statPrint = '\n'
    namesArr = [len(str(stat[0])) for stat in stats.func_stats]
    log.debug("namesArr %s", str(namesArr))
    maxNameLen = max(namesArr)
    log.debug("maxNameLen: %s", maxNameLen)

    for stat in stats.func_stats:
        nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))]
        log.debug('nameAppendSpaces: %s', nameAppendSpaces)
        blankSpace = ''
        for space in nameAppendSpaces:
            blankSpace += space

        log.debug("adding spaces: %s", len(nameAppendSpaces))
        statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str(
            round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n"

    log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub")
    log.log(1000, statPrint)

Em seguida, quando o programa funcionar, você poderá iniciar o criador de perfil a qualquer momento chamando o startProfilermétodo RPC e despejar informações de criação de perfil em um arquivo de log chamando printProfiler(ou modifique o método rpc para devolvê-lo ao chamador) e obter essa saída:

2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
name                                                                                                                                      ncall     ttot    tsub
2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
C:\Python27\lib\sched.py.run:80                                                                                                           22        0.11    0.05
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293                                                22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515                                                    22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66                                       1         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.date_time_string:464                                                                                    1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243     4         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537                                                                          1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4         0.0     0.0
<string>.__new__:8                                                                                                                        220       0.0     0.0
C:\Python27\lib\socket.py.close:276                                                                                                       4         0.0     0.0
C:\Python27\lib\threading.py.__init__:558                                                                                                 1         0.0     0.0
<string>.__new__:8                                                                                                                        4         0.0     0.0
C:\Python27\lib\threading.py.notify:372                                                                                                   1         0.0     0.0
C:\Python27\lib\rfc822.py.getheader:285                                                                                                   4         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301                                                                                  1         0.0     0.0
C:\Python27\lib\xmlrpclib.py.end:816                                                                                                      3         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467                                                                                         1         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460                                                                               1         0.0     0.0
C:\Python27\lib\SocketServer.py.close_request:475                                                                                         1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\__init__.py.cpu_times:1066               4         0.0     0.0 

Pode não ser muito útil para scripts curtos, mas ajuda a otimizar processos do tipo servidor, especialmente porque o printProfilermétodo pode ser chamado várias vezes ao longo do tempo para criar um perfil e comparar, por exemplo, diferentes cenários de uso do programa.

Nas versões mais recentes do yappi, o seguinte código funcionará:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()
Mr. Girgitt
fonte
Não deveria ser chamado de Yappi estupendo?
Therealstubot
Infelizmente, o código acima funciona apenas com a versão 0.62, que não está disponível no pypy. O módulo precisa ser compilado a partir de 0,62 fontes disponíveis aqui: github.com/nirs/yappi/releases ou usar a compilação que fiz para o Windows no repositório
Sr. Girgitt
a compatibilidade com a versão 1.0 pode ser facilmente fornecida - pelo menos para a saída de impressão - modificando a função printProfiler: def printProfiler(): if not yappi_available: return stats = yappi.get_func_stats() stats.print_all(columns={0:("name",90), 1:("ncall", 5), 2:("tsub", 8), 3:("ttot", 8), 4:("tavg",8)}) (OK, depois de algumas vezes inserir o bloco de código no comentário que desisti. isso é incrivelmente difícil para um site de perguntas e respostas orientado à programação. )
Sr. Girgitt 26/11/19
4

Uma nova ferramenta para lidar com criação de perfil em Python é o PyVmMonitor: http://www.pyvmmonitor.com/

Possui alguns recursos exclusivos, como

  • Anexar criador de perfil a um programa em execução (CPython)
  • Perfil sob demanda com integração Yappi
  • Perfil em uma máquina diferente
  • Suporte a múltiplos processos (multiprocessamento, django ...)
  • Amostragem ao vivo / visualização da CPU (com seleção de intervalo de tempo)
  • Perfil determinístico através da integração cProfile / profile
  • Analisar os resultados existentes do PStats
  • Arquivos DOT abertos
  • Acesso à API programática
  • Agrupe amostras por método ou linha
  • Integração com PyDev
  • Integração PyCharm

Nota: é comercial, mas gratuito para código aberto.

Fabio Zadrozny
fonte
4

gprof2dot_magic

Função mágica para gprof2dotcriar o perfil de qualquer instrução Python como um gráfico DOT no JupyterLab ou no Jupyter Notebook.

insira a descrição da imagem aqui

Repo GitHub: https://github.com/mattijn/gprof2dot_magic

instalação

Verifique se você tem o pacote Python gprof2dot_magic.

pip install gprof2dot_magic

Suas dependências gprof2dote graphvizserão instaladas também

uso

Para ativar a função mágica, primeiro carregue o gprof2dot_magicmódulo

%load_ext gprof2dot_magic

e então perfil qualquer declaração de linha como um gráfico DOT como tal:

%gprof2dot print('hello world')

insira a descrição da imagem aqui

Mattijn
fonte
3

Sempre quis saber o que diabos esse script python está fazendo? Digite o shell de inspeção. Inspecionar Shell permite imprimir / alterar globais e executar funções sem interromper o script em execução. Agora com auto-complete e histórico de comandos (apenas no linux).

Inspecione o Shell não é um depurador no estilo pdb.

https://github.com/amoffat/Inspect-Shell

Você poderia usar isso (e seu relógio de pulso).

Coronel Panic
fonte
3

Isso dependeria do que você deseja ver na criação de perfil. Métricas simples de tempo podem ser fornecidas por (bash).

time python python_prog.py

Mesmo '/ usr / bin / time' pode gerar métricas detalhadas usando o sinalizador '--verbose'.

Para verificar as métricas de tempo fornecidas por cada função e entender melhor quanto tempo é gasto nas funções, você pode usar o cProfile embutido em python.

Entrando em métricas mais detalhadas, como desempenho, o tempo não é a única métrica. Você pode se preocupar com memória, threads, etc.
Opções de criação de perfil:
1. line_profiler é outro gerador de perfil usado normalmente para descobrir métricas de tempo linha por linha.
2. memory_profiler é uma ferramenta para analisar o uso de memória.
3. heapy (do projeto Guppy) Crie um perfil de como os objetos na pilha são usados.

Estes são alguns dos mais comuns que costumo usar. Mas se você quiser descobrir mais, tente ler este livro. É um livro muito bom sobre como começar com o desempenho em mente. Você pode passar para tópicos avançados sobre o uso de python compilado Cython e JIT (Just-in-time).

VishalMishra
fonte
2

Com um criador de perfil estatístico como austin , nenhuma instrumentação é necessária, o que significa que você pode obter dados de criação de perfil de um aplicativo Python simplesmente com

austin python3 my_script.py

A saída bruta não é muito útil, mas você pode canalizar isso para o flamegraph.pl para obter uma representação gráfica dos dados que fornece uma descrição de onde o tempo (medido em microssegundos de tempo real) está sendo gasto.

austin python3 my_script.py | flamegraph.pl > my_script_profile.svg
Phoenix87
fonte
2

Para obter estatísticas rápidas do perfil em um notebook IPython. Pode-se incorporar line_profiler e memory_profiler em linha reta em seus notebooks.

Outro pacote útil é o Pympler . É um poderoso pacote de criação de perfil capaz de rastrear classes, objetos, funções, vazamentos de memória etc. Exemplos abaixo, documentos anexados.

Pegue!

!pip install line_profiler
!pip install memory_profiler
!pip install pympler

Carregue-o!

%load_ext line_profiler
%load_ext memory_profiler

Use-o!


%Tempo

%time print('Outputs CPU time,Wall Clock time') 
#CPU times: user 2 µs, sys: 0 ns, total: 2 µs Wall time: 5.96 µs

Dá:

  • Tempos de CPU: tempo de execução no nível da CPU
  • sys times: tempo de execução no nível do sistema
  • total: tempo da CPU + hora do sistema
  • Wall time: Relógio de parede

% timeit

%timeit -r 7 -n 1000 print('Outputs execution time of the snippet') 
#1000 loops, best of 7: 7.46 ns per loop
  • Dá o melhor tempo para determinado número de execuções (r) em loop (n) vezes.
  • Mostra detalhes sobre o cache do sistema:
    • Quando trechos de código são executados várias vezes, o sistema armazena em cache algumas operações e não as executa novamente, o que pode prejudicar a precisão dos relatórios de perfil.

% de poda

%prun -s cumulative 'Code to profile' 

Dá:

  • número de chamadas de função (ncalls)
  • possui entradas por chamada de função (distintas)
  • tempo gasto por chamada (percall)
  • tempo decorrido até a chamada de função (cumtime)
  • nome do func / module chamado etc ...

Perfil cumulativo


% memit

%memit 'Code to profile'
#peak memory: 199.45 MiB, increment: 0.00 MiB

Dá:

  • Uso de memória

% lprun

#Example function
def fun():
  for i in range(10):
    print(i)

#Usage: %lprun <name_of_the_function> function
%lprun -f fun fun()

Dá:

  • Estatísticas de linha

LineProfile


sys.getsizeof

sys.getsizeof('code to profile')
# 64 bytes

Retorna o tamanho de um objeto em bytes.


asizeof () do pympler

from pympler import asizeof
obj = [1,2,("hey","ha"),3]
print(asizeof.asizeof(obj,stats=4))

pympler.asizeof pode ser usado para investigar quanta memória determinados objetos Python consomem. Em contraste com sys.getsizeof, um tamanho de objetos recursivamente

pympler.asizeof


rastreador do pympler

from pympler import tracker
tr = tracker.SummaryTracker()
def fun():
  li = [1,2,3]
  di = {"ha":"haha","duh":"Umm"}
fun()
tr.print_diff()

Rastreia a vida útil de uma função.

saída rastreador

O pacote Pympler consiste em um grande número de funções de alta utilidade para codificar o perfil. Tudo isso não pode ser coberto aqui. Consulte a documentação anexada para implementações de perfil detalhado.

Pympler doc

Aditya Patnaik
fonte
1

Há também um perfilador estatístico chamado statprof. É um criador de perfil de amostragem, por isso adiciona uma sobrecarga mínima ao seu código e fornece tempos baseados em linhas (não apenas baseados em funções). É mais adequado para aplicativos em tempo real, como jogos, mas pode ter menos precisão que o cProfile.

A versão no pypi é um pouco antiga, portanto, é possível instalá-la pipespecificando o repositório git :

pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01

Você pode executá-lo assim:

import statprof

with statprof.profile():
    my_questionable_function()

Consulte também https://stackoverflow.com/a/10333592/320036

z0r
fonte
1

Acabei de desenvolver meu próprio criador de perfil inspirado em pypref_time:

https://github.com/modaresimr/auto_profiler

Ao adicionar um decorador, ele mostrará uma árvore de funções demoradas

@Profiler(depth=4, on_disable=show)

Install by: pip install auto_profiler

Exemplo

import time # line number 1
import random

from auto_profiler import Profiler, Tree

def f1():
    mysleep(.6+random.random())

def mysleep(t):
    time.sleep(t)

def fact(i):
    f1()
    if(i==1):
        return 1
    return i*fact(i-1)


def show(p):
    print('Time   [Hits * PerHit] Function name [Called from] [Function Location]\n'+\
          '-----------------------------------------------------------------------')
    print(Tree(p.root, threshold=0.5))

@Profiler(depth=4, on_disable=show)
def main():
    for i in range(5):
        f1()

    fact(3)


if __name__ == '__main__':
    main()

Saída de exemplo


Time   [Hits * PerHit] Function name [Called from] [function location]
-----------------------------------------------------------------------
8.974s [1 * 8.974]  main  [auto-profiler/profiler.py:267]  [/test/t2.py:30]
├── 5.954s [5 * 1.191]  f1  [/test/t2.py:34]  [/test/t2.py:14]
   └── 5.954s [5 * 1.191]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
       └── 5.954s [5 * 1.191]  <time.sleep>
|
|
|   # The rest is for the example recursive function call fact
└── 3.020s [1 * 3.020]  fact  [/test/t2.py:36]  [/test/t2.py:20]
    ├── 0.849s [1 * 0.849]  f1  [/test/t2.py:21]  [/test/t2.py:14]
       └── 0.849s [1 * 0.849]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
           └── 0.849s [1 * 0.849]  <time.sleep>
    └── 2.171s [1 * 2.171]  fact  [/test/t2.py:24]  [/test/t2.py:20]
        ├── 1.552s [1 * 1.552]  f1  [/test/t2.py:21]  [/test/t2.py:14]
           └── 1.552s [1 * 1.552]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
        └── 0.619s [1 * 0.619]  fact  [/test/t2.py:24]  [/test/t2.py:20]
            └── 0.619s [1 * 0.619]  f1  [/test/t2.py:21]  [/test/t2.py:14]
Todos
fonte
0

Quando não sou root no servidor, uso lsprofcalltree.py e executo meu programa da seguinte maneira:

python lsprofcalltree.py -o callgrind.1 test.py

Então, posso abrir o relatório com qualquer software compatível com callgrind , como qcachegrind

Vincent Fenet
fonte