¿Cómo puedes perfilar un script de Python?

1283

El Proyecto Euler y otros concursos de codificación a menudo tienen un tiempo máximo para ejecutarse o las personas se jactan de lo rápido que se ejecuta su solución particular. Con Python, a veces los enfoques son un tanto torpes, es decir, agregar código de tiempo a __main__.

¿Cuál es una buena manera de perfilar cuánto tiempo tarda un programa Python en ejecutarse?

Chris Lawlor
fuente
113
Los programas del proyecto euler no deberían necesitar perfiles. O tiene un algoritmo que funciona en menos de un minuto, o tiene un algoritmo completamente incorrecto. "Tuning" rara vez es apropiado. Por lo general, debe adoptar un enfoque nuevo.
S.Lott
105
S.Lott: La creación de perfiles suele ser una forma útil de determinar qué subrutinas son lentas. Las subrutinas que llevan mucho tiempo son excelentes candidatos para la mejora algorítmica.
stalepretzel

Respuestas:

1370

Python incluye un generador de perfiles llamado cProfile . No solo proporciona el tiempo total de ejecución, sino que también multiplica cada función por separado y le indica cuántas veces se llamó a cada función, lo que facilita determinar dónde debe realizar las optimizaciones.

Puede llamarlo desde su código, o desde el intérprete, así:

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

Aún más útil, puede invocar el cProfile cuando ejecuta un script:

python -m cProfile myscript.py

Para hacerlo aún más fácil, hice un pequeño archivo por lotes llamado 'profile.bat':

python -m cProfile %1

Entonces todo lo que tengo que hacer es correr:

profile euler048.py

Y entiendo esto:

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}

EDITAR: Enlace actualizado a un buen recurso de video de PyCon 2013 titulado Python Profiling
También a través de YouTube .

Chris Lawlor
fuente
251
También es útil ordenar los resultados, que se pueden hacer con el interruptor -s, por ejemplo: '-s time'. Puede utilizar las opciones de clasificación acumulativa / nombre / hora / archivo.
Jiri
19
También vale la pena señalar que puede usar el módulo cProfile de ipython usando la función mágica% prun (ejecución de perfil). Primero importe su módulo y luego llame a la función principal con% prun: import euler048; % prun euler048.main ()
RussellStewart
53
Para visualizar los volcados de cProfile (creados por python -m cProfile -o <out.profile> <script>), RunSnakeRun , invocado como runsnake <out.profile>es invaluable.
Lily Chung
13
@NeilG incluso para Python 3, cprofiletodavía se recomienda más profile.
trichoplax
17
Para visualizar los volcados de cProfile, RunSnakeRun no se ha actualizado desde 2011 y no es compatible con python3. Deberías usar snakeviz en su lugar
Giacomo Tecya Pigani
424

Hace un tiempo hice pycallgraphque genera una visualización a partir de su código Python. Editar: He actualizado el ejemplo para que funcione con 3.3, la última versión de este escrito.

Después de pip install pycallgraphinstalar e GraphViz , puede ejecutarlo desde la línea de comandos:

pycallgraph graphviz -- ./mypythonscript.py

O bien, puede perfilar partes particulares de su código:

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

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

Cualquiera de estos generará un pycallgraph.pngarchivo similar a la imagen a continuación:

ingrese la descripción de la imagen aquí

gak
fuente
43
¿Estás coloreando según la cantidad de llamadas? Si es así, debe colorear según el tiempo porque la función con más llamadas no siempre es la que lleva más tiempo.
rojo
21
@red Puede personalizar los colores como desee, e incluso de forma independiente para cada medición. Por ejemplo, rojo para llamadas, azul para tiempo, verde para uso de memoria.
gak
2
recibiendo este errorTraceback (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
Actualicé esto para mencionar que necesita instalar GraphViz para que las cosas funcionen como se describe. En Ubuntu esto es justo sudo apt-get install graphviz.
mlissner
2
Esto requiere un poco de trabajo para instalar. Aquí hay 3 pasos para ayudar. 1. Instalar a través de pip, 2. Instalar GraphViz a través de exe 3. Configurar variables de ruta al directorio GraphViz 4. Descubrir cómo solucionar todos los demás errores. 5. ¿Dónde se guarda el archivo png?
pantano
199

Vale la pena señalar que usar el generador de perfiles solo funciona (de forma predeterminada) en el hilo principal, y no obtendrá ninguna información de otros hilos si los usa. Esto puede ser un poco un problema ya que no se menciona por completo en la documentación del generador de perfiles .

Si también desea perfilar hilos, querrá ver la threading.setprofile()función en los documentos.

También puede crear su propia threading.Threadsubclase para hacerlo:

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,))

y usa esa ProfiledThreadclase en lugar de la estándar. Puede darle más flexibilidad, pero no estoy seguro de que valga la pena, especialmente si está utilizando un código de terceros que no usaría su clase.

Joe Shaw
fuente
1
Tampoco veo ninguna referencia a runcall en la documentación. Dando un vistazo a cProfile.py, no estoy seguro de por qué usa la función threading.Thread.run ni self como argumento. Esperaba ver una referencia al método de ejecución de otro hilo aquí.
PypeBros
No está en la documentación, pero está en el módulo. Consulte hg.python.org/cpython/file/6bf07db23445/Lib/cProfile.py#l140 . Eso le permite perfilar una llamada de función específica, y en nuestro caso queremos perfilar la targetfunción de Thread , que es lo que threading.Thread.run()ejecuta la llamada. Pero como dije en la respuesta, probablemente no valga la pena subclasificar Thread, ya que cualquier código de terceros no lo usará, y en su lugar lo usará threading.setprofile().
Joe Shaw
99
parece que el ajuste del código con profiler.enable () y profiler.disable () también funciona bastante bien. Eso es básicamente lo que hace runcall y no impone ningún número de argumentos o cosas similares.
PypeBros
1
Joe, ¿sabes cómo juega el perfilador con asyncio en Python 3.4?
Nick Chammas
149

El wiki de Python es una gran página para la creación de perfiles de recursos: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

al igual que los documentos de Python: http://docs.python.org/library/profile.html

Como lo muestra Chris Lawlor, cProfile es una gran herramienta y se puede usar fácilmente para imprimir en la pantalla:

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

o para archivar:

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

PD> Si estás usando Ubuntu, asegúrate de instalar python-profile

apt-get install python-profiler 

Si sale al archivo, puede obtener visualizaciones agradables utilizando las siguientes herramientas

PyCallGraph: una herramienta para crear imágenes de gráficos de llamadas
instalar:

 pip install pycallgraph

correr:

 pycallgraph mine.py args

ver:

 gimp pycallgraph.png

Puedes usar lo que quieras para ver el archivo png, usé gimp
Desafortunadamente a menudo obtengo

dot: graph es demasiado grande para mapas de bits de cairo-render. Escalando en 0.257079 para adaptarse

lo que hace que mis imágenes sean inusualmente pequeñas. Así que generalmente creo archivos svg:

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

PS> asegúrese de instalar graphviz (que proporciona el programa de puntos):

pip install graphviz

Gráficos alternativos usando gprof2dot a través de @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
fuente
12
gprof2dot también puede hacer esos gráficos. Creo que la salida es un poco mejor ( ejemplo ).
maxy
2
Graphviz también es necesario si está utilizando OSX
Vaibhav Mishra
134

El comentario de @ Maxy sobre esta respuesta me ayudó lo suficiente como para pensar que merece su propia respuesta: ya tenía archivos .pstats generados por cProfile y no quería volver a ejecutar las cosas con pycallgraph, así que usé gprof2dot , y me volví 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

y BLAM!

Utiliza punto (lo mismo que usa pycallgraph), por lo que la salida se ve similar. Sin embargo, tengo la impresión de que gprof2dot pierde menos información:

Salida de ejemplo de gprof2dot

quodlibetor
fuente
1
Buen enfoque, funciona realmente bien, ya que puede ver SVG en Chrome, etc. y escalarlo hacia arriba / abajo. La tercera línea tiene un error tipográfico, debe ser: ln -s pwd/gprof2dot/gprof2dot.py $ HOME / bin (o use ln -s $ PWD / gprof2dot / gprof2dot.py ~ / bin en la mayoría de los shells - el acento grave se toma como formato en primer lugar versión).
RichVel
2
Ah, buen punto. Me lnequivoco en el orden de los argumentos casi siempre.
quodlibetor
77
el truco es recordar que ln y cp tienen el mismo orden de argumento: piense en ello como 'copiar archivo1 a archivo2 o dir2, pero hacer un enlace'
RichVel
Eso tiene sentido, creo que el uso de "TARGET" en la página de manual me arroja.
quodlibetor
Por favor, ¿cómo obtuviste esquinas redondeadas? Siento que mejora la legibilidad. Solo obtengo esquinas feas y afiladas que no son geniales en presencia de muchos bordes alrededor de las cajas.
Hibou57
78

Me encontré con una herramienta útil llamada SnakeViz cuando investigaba este tema. SnakeViz es una herramienta de visualización de perfiles basada en la web. Es muy fácil de instalar y usar. La forma habitual en que lo uso es generar un archivo de estadísticas %pruny luego hacer análisis en SnakeViz.

La técnica de visualización principal utilizada es el gráfico Sunburst como se muestra a continuación, en el que la jerarquía de las llamadas a funciones se organiza como capas de arcos e información de tiempo codificada en sus anchos angulares.

Lo mejor es que puedes interactuar con el gráfico. Por ejemplo, para acercar uno puede hacer clic en un arco, y el arco y sus descendientes se agrandarán como un nuevo resplandor solar para mostrar más detalles.

ingrese la descripción de la imagen aquí

zaxliu
fuente
1
La respuesta de CodeCabbie incluye las instrucciones de instalación (cortas) y muestra cómo (fácilmente) usar SnakeViz.
Oren Milman
Aquí he leído una buena guía de la OMI sobre cómo usar la creación de perfiles para Python en el cuaderno jupyter
Alexei Martianov
73

La forma más simple y rápida de encontrar a dónde va todo el tiempo.

1. pip install snakeviz

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

3. snakeviz temp.dat

Dibuja un gráfico circular en un navegador. La pieza más grande es la función del problema. Muy simple.

CodeCabbie
fuente
1
Esto fue muy útil. Gracias.
jippyjoe4
55

Creo que cProfilees excelente para crear perfiles, mientras que kcachegrindes excelente para visualizar los resultados. El pyprof2calltreeintermedio maneja la conversión del archivo.

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

Para instalar las herramientas necesarias (en Ubuntu, al menos):

apt-get install kcachegrind
pip install pyprof2calltree

El resultado:

Captura de pantalla del resultado.

Federico
fuente
99
Usuarios de Mac instalar brew install qcachegrindy substitude cada uno kcachegrind con qcachegrind en la descripción de perfiles de éxito.
Kevin Katzke
Tuve que hacer esto para que funcione:export QT_X11_NO_MITSHM=1
Yonatan Simson
41

También vale la pena mencionar el visor de volcado de cProfile de GUI RunSnakeRun . Le permite ordenar y seleccionar, haciendo zoom en las partes relevantes del programa. El tamaño de los rectángulos en la imagen es proporcional al tiempo empleado. Si pasa el mouse sobre un rectángulo, resalta esa llamada en la tabla y en todas partes del mapa. Cuando hace doble clic en un rectángulo, se acerca a esa parte. Le mostrará quién llama a esa porción y qué llama esa porción.

La información descriptiva es muy útil. Le muestra el código para ese bit que puede ser útil cuando se trata de llamadas de biblioteca incorporadas. Te dice qué archivo y qué línea encontrar el código.

También quiero señalar que el OP dijo 'perfilado' pero parece que se refería a 'tiempo'. Tenga en cuenta que los programas se ejecutarán más lentamente cuando se perfilen.

ingrese la descripción de la imagen aquí

Pete
fuente
34

Recientemente creé atún para visualizar Python runtime e importar perfiles; Esto puede ser útil aquí.

ingrese la descripción de la imagen aquí

Instalar con

pip install tuna

Crear un perfil de tiempo de ejecución

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

o un perfil de importación (se requiere Python 3.7+)

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

Luego solo ejecuta atún en el archivo

tuna program.prof
Nico Schlömer
fuente
33

Un buen módulo de creación de perfiles es line_profiler (llamado mediante el script kernprof.py). Se puede descargar aquí .

Entiendo que cProfile solo brinda información sobre el tiempo total empleado en cada función. Por lo tanto, las líneas individuales de código no están cronometradas. Este es un problema en la informática científica, ya que a menudo una sola línea puede llevar mucho tiempo. Además, como recuerdo, cProfile no captó el tiempo que pasaba en decir numpy.dot.

Ian Langmore
fuente
33

pprofile

line_profiler(ya presentado aquí) también inspirado pprofile, que se describe como:

Granularidad de línea, determinista de subprocesos y analizador estadístico de python puro

Proporciona granularidad de línea, ya que line_profileres Python puro, se puede usar como un comando independiente o un módulo, e incluso puede generar archivos de formato callgrind que se pueden analizar fácilmente [k|q]cachegrind.

vprof

También hay vprof , un paquete de Python descrito como:

[...] proporciona visualizaciones ricas e interactivas para diversas características del programa Python, como el tiempo de ejecución y el uso de memoria.

mapa de calor

BenC
fuente
14

Hay muchas respuestas excelentes, pero usan la línea de comandos o algún programa externo para perfilar y / u ordenar los resultados.

Realmente eché de menos alguna forma de usar mi IDE (eclipse-PyDev) sin tocar la línea de comando o instalar nada. Asi que aqui esta.

Perfilado sin línea 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)

Ver documentos u otras respuestas para más información.

David Mašek
fuente
por ejemplo, el perfil imprime {mapa} o {xxx}. ¿cómo sé que se llama al método {xxx} desde qué archivo? mi perfil imprime {método 'comprimir' de 'zlib.Compress' objetos} lleva la mayor parte del tiempo, pero no uso ningún zlib, por lo que supongo que algunas funciones de llamada numpy pueden usarlo. ¿Cómo sé cuál es exactamente el archivo y la línea que lleva mucho tiempo?
machen
12

Siguiendo la respuesta de Joe Shaw sobre el código multiproceso que no funciona como se esperaba, me di cuenta de que el runcallmétodo en cProfile es simplemente hacer self.enable()y self.disable()llama a la llamada de función perfilada, por lo que puede hacerlo usted mismo y tener el código que desee en el medio mínima interferencia con el código existente.

PypeBros
fuente
3
Excelente consejo! Un vistazo rápido al cprofile.pycódigo fuente revela que eso es exactamente lo que runcall()hace. Siendo más específico, después de crear una instancia de Perfil con prof = cprofile.Profile(), llame inmediatamente prof.disable(), y luego simplemente agregue prof.enable()y prof.disable()llame alrededor de la sección de código que desea perfilar.
Martineau
Esto es muy útil, pero parece que el código que está realmente entre habilitar y deshabilitar no está perfilado , solo las funciones que llama. ¿Tengo este derecho? Tendría que ajustar ese código en una llamada de función para que cuente para cualquiera de los números en print_stats ().
Bob Stein
10

En la fuente de Virtaal hay una clase y un decorador muy útiles que pueden facilitar la creación de perfiles (incluso para métodos / funciones específicos). La salida se puede ver muy cómodamente en KCacheGrind.

Walter
fuente
1
Gracias por esta joya FYI: Esto se puede usar como un módulo independiente con cualquier código, no se requiere la base de código Virtaal. Simplemente guarde el archivo en profiling.py e importe el profile_func (). Use @profile_func () como decorador de cualquier función que necesite para perfilar y viola. :)
Amjith
9

cProfile es excelente para la creación de perfiles rápidos, pero la mayoría de las veces me terminaba con los errores. La función runctx resuelve este problema al inicializar correctamente el entorno y las variables, espero que pueda ser útil para alguien:

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

Si desea hacer un generador de perfiles acumulativo, lo que significa ejecutar la función varias veces seguidas y ver la suma de los resultados.

puedes usar este cumulative_profilerdecorador:

es python> = 3.6 específico, pero puede eliminarlo nonlocalpara que funcione en versiones anteriores.

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

Ejemplo

perfilando la función baz

import time

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

baz()

baz corrió 5 veces e imprimió esto:

         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 la cantidad de veces

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

La solución solo para terminal (y la más simple), en caso de que todas esas UI sofisticadas no se instalen o ejecuten:
ignórelas por cProfilecompleto y reemplácelas pyinstrument, que recolectará y mostrará el árbol de llamadas justo después de la ejecución.

Instalar en pc:

$ pip install pyinstrument

Perfil y resultado de visualización:

$ python -m pyinstrument ./prog.py

Funciona con python2 y 3.

[EDITAR] La documentación de la API, para perfilar solo una parte del código, se puede encontrar aquí .

Francois
fuente
6

Mi forma es usar yappi ( https://github.com/sumerc/yappi ). Es especialmente útil combinado con un servidor RPC donde (incluso solo para la depuración) registra el método para iniciar, detener e imprimir información de perfil, por ejemplo, de esta manera:

@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)

Luego, cuando su programa funcione, puede iniciar el generador de perfiles en cualquier momento llamando al startProfilermétodo RPC y volcar la información de perfil en un archivo de registro llamando printProfiler(o modificar el método rpc para devolverlo al llamante) y obtener dicha salida:

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 

Puede que no sea muy útil para scripts cortos, pero ayuda a optimizar los procesos de tipo servidor, especialmente dado que el printProfilermétodo puede llamarse varias veces a lo largo del tiempo para perfilar y comparar, por ejemplo, diferentes escenarios de uso del programa.

En las versiones más recientes de yappi, el siguiente código funcionará:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()
Señor Girgitt
fuente
¿No debería llamarse Stupendous Yappi?
Therealstubot
Lamentablemente, el código anterior solo funciona con la versión 0.62 que no está disponible en pypy. El módulo debe compilarse a partir de 0.62 fuentes disponibles aquí: github.com/nirs/yappi/releases o use build que hice para Windows en repositorio bifurcado para ese propósito github.com/Girgitt/yappi/releases
Mr. Girgitt
La compatibilidad con la versión 1.0 se puede proporcionar fácilmente, al menos para la salida de impresión, modificando la función 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 después de intentar un par de veces para insertar el bloque de código en el comentario que di). Esto es increíblemente difícil para un sitio de preguntas y respuestas orientado a la programación. )
Sr. Girgitt el
4

Una nueva herramienta para manejar la creación de perfiles en Python es PyVmMonitor: http://www.pyvmmonitor.com/

Tiene algunas características únicas como

  • Adjunte el generador de perfiles a un programa en ejecución (CPython)
  • Perfiles bajo demanda con integración de Yappi
  • Perfil en una máquina diferente
  • Soporte de múltiples procesos (multiprocesamiento, django ...)
  • Muestreo en vivo / vista de CPU (con selección de rango de tiempo)
  • Perfiles deterministas a través de la integración de perfil / perfil c
  • Analizar resultados de PStats existentes
  • Abrir archivos DOT
  • Acceso programático a la API
  • Agrupar muestras por método o línea
  • Integración PyDev
  • Integración PyCharm

Nota: es comercial, pero gratis para código abierto.

Fabio Zadrozny
fuente
4

gprof2dot_magic

Función mágica para gprof2dotperfilar cualquier declaración de Python como un gráfico DOT en JupyterLab o Jupyter Notebook.

ingrese la descripción de la imagen aquí

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

instalación

Asegúrate de tener el paquete Python gprof2dot_magic.

pip install gprof2dot_magic

Sus dependencias gprof2doty graphvizse instalarán también

uso

Para habilitar la función mágica, primero cargue el gprof2dot_magicmódulo

%load_ext gprof2dot_magic

y luego perfile cualquier declaración de línea como un gráfico DOT como tal:

%gprof2dot print('hello world')

ingrese la descripción de la imagen aquí

Mattijn
fuente
3

¿Alguna vez quisiste saber qué demonios está haciendo ese script de Python? Ingrese el Shell de inspección. Inspeccionar Shell le permite imprimir / alterar globales y ejecutar funciones sin interrumpir el script en ejecución. Ahora con autocompletar e historial de comandos (solo en Linux).

Inspeccionar Shell no es un depurador de estilo pdb.

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

Podrías usar eso (y tu reloj de pulsera).

Coronel Panic
fuente
3

Dependerá de lo que quiera ver de los perfiles. Las métricas de tiempo simples pueden ser dadas por (bash).

time python python_prog.py

Incluso '/ usr / bin / time' puede generar métricas detalladas utilizando el indicador '--verbose'.

Para verificar las métricas de tiempo proporcionadas por cada función y comprender mejor cuánto tiempo se dedica a las funciones, puede usar el cProfile incorporado en python.

Al entrar en métricas más detalladas como el rendimiento, el tiempo no es la única métrica. Puede preocuparse por la memoria, los subprocesos, etc.
Opciones de creación de perfiles:
1. line_profiler es otro generador de perfiles que se usa comúnmente para averiguar métricas de temporización línea por línea.
2. memory_profiler es una herramienta para perfilar el uso de memoria.
3. heapy (del proyecto Guppy) Perfil de cómo se utilizan los objetos en el montón.

Estos son algunos de los más comunes que suelo usar. Pero si desea obtener más información, intente leer este libro. Es un libro bastante bueno para comenzar teniendo en cuenta el rendimiento. Puede pasar a temas avanzados sobre el uso de Python compilado Cython y JIT (Just-in-time).

VishalMishra
fuente
2

Con un generador de perfiles estadísticos como Austin , no se requiere instrumentación, lo que significa que puede obtener datos de perfiles de una aplicación Python simplemente con

austin python3 my_script.py

La salida en bruto no es muy útil, pero puede canalizar eso a flamegraph.pl para obtener una representación gráfica de la llama de esos datos que le da un desglose de dónde se está gastando el tiempo (medido en microsegundos de tiempo real).

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

Para obtener estadísticas de perfil rápidas en una notebook IPython. Uno puede integrar line_profiler y memory_profiler directamente en sus cuadernos.

Otro paquete útil es Pympler . Es un poderoso paquete de creación de perfiles que es capaz de rastrear clases, objetos, funciones, pérdidas de memoria, etc. Ejemplos a continuación, Documentos adjuntos.

¡Consíguelo!

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

¡Cárgalo!

%load_ext line_profiler
%load_ext memory_profiler

Úsalo!


%hora

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

Da:

  • Tiempos de CPU: tiempo de ejecución a nivel de CPU
  • tiempos sys: tiempo de ejecución a nivel del sistema
  • total: tiempo de CPU + tiempo del sistema
  • Tiempo de pared: tiempo de reloj de pared

%cronométralo

%timeit -r 7 -n 1000 print('Outputs execution time of the snippet') 
#1000 loops, best of 7: 7.46 ns per loop
  • Da el mejor tiempo del número dado de ejecuciones (r) en bucles (n) veces.
  • Detalles de salidas en el almacenamiento en caché del sistema:
    • Cuando los fragmentos de código se ejecutan varias veces, el sistema almacena en caché algunas operaciones y no las ejecuta nuevamente, lo que puede dificultar la precisión de los informes de perfil.

% poda

%prun -s cumulative 'Code to profile' 

Da:

  • cantidad de llamadas a funciones (ncalls)
  • tiene entradas por llamada de función (distinta)
  • tiempo empleado por llamada (percall)
  • tiempo transcurrido hasta esa llamada de función (cumtime)
  • nombre del func / módulo llamado etc ...

Perfil acumulativo


% memit

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

Da:

  • Uso de memoria

% lprun

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

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

Da:

  • Estadísticas sabias de línea

LineProfile


sys.getsizeof

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

Devuelve el tamaño de un objeto en bytes.


asizeof () de pympler

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

pympler.asizeof se puede usar para investigar cuánta memoria consumen ciertos objetos de Python. A diferencia de sys.getsizeof, asizeof dimensiona objetos de forma recursiva

pympler.asizeof


rastreador de pympler

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

Realiza un seguimiento de la vida útil de una función.

salida del rastreador

El paquete Pympler consta de una gran cantidad de funciones de alta utilidad para perfilar el código. Todo lo cual no puede ser cubierto aquí. Consulte la documentación adjunta para ver las implementaciones detalladas del perfil.

Pympler doc

Aditya Patnaik
fuente
1

También hay un generador de perfiles estadísticos llamado statprof. Es un generador de perfiles de muestreo, por lo que agrega una sobrecarga mínima a su código y proporciona tiempos basados ​​en líneas (no solo en función). Es más adecuado para aplicaciones suaves en tiempo real como juegos, pero puede tener menos precisión que cProfile.

La versión en pypi es un poco antigua, por lo que puede instalarla pipespecificando el repositorio git :

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

Puedes ejecutarlo así:

import statprof

with statprof.profile():
    my_questionable_function()

Ver también https://stackoverflow.com/a/10333592/320036

z0r
fuente
1

Acabo de desarrollar mi propio generador de perfiles inspirado en pypref_time:

https://github.com/modaresimr/auto_profiler

Al agregar un decorador, mostrará un árbol de funciones que consumen mucho tiempo

@Profiler(depth=4, on_disable=show)

Install by: pip install auto_profiler

Ejemplo

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()

Salida de ejemplo


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]
Ali
fuente
0

Cuando no soy root en el servidor, uso lsprofcalltree.py y ejecuto mi programa así:

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

Luego puedo abrir el informe con cualquier software compatible con callgrind, como qcachegrind

Vincent Fenet
fuente