¿Cómo puedes hacer un perfil de un script de Python?

El proyecto Euler y otros concursos de encoding a menudo tienen un tiempo máximo de ejecución o la gente se jacta de la rapidez con la que se ejecuta su solución particular. Con python, a veces los enfoques son un tanto confusos, es decir, añadiendo un código de tiempo a __main__ .

¿Cuál es una buena manera de determinar cuánto tarda en ejecutarse un progtwig python?

Python incluye un generador de perfiles llamado cProfile . No solo proporciona el tiempo total de ejecución, sino que también cronometra cada función por separado, y le dice 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 

Así que 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 :1() 1000 0.051 0.000 0.051 0.000 euler048.py:2() 1 0.005 0.005 0.061 0.061 euler048.py:2() 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 .

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

Después de un pip install pycallgraph e instalando GraphViz , puede ejecutarlo desde la línea de comando:

 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 archivo pycallgraph.png similar a la imagen a continuación:

introduzca la descripción de la imagen aquí

Vale la pena señalar que el uso del generador de perfiles solo funciona (de forma predeterminada) en el subproceso principal, y no obtendrá información de otros subprocesos si los utiliza. Esto puede ser un poco complicado, ya que no se menciona completamente en la documentación del generador de perfiles .

Si también desea crear perfiles de subprocesos, querrá ver la función threading.setprofile() en los documentos.

También puedes crear tu propio threading.Thread Subclase threading.Thread 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 clase ProfiledThread lugar de la estándar. Podría 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.

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

como es la documentación 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  

o archivar:

 python -m cProfile -o output.file mine.py  

PS> Si está utilizando Ubuntu, asegúrese de instalar python-profile

 sudo apt-get install python-profiler 

Si imprime en un archivo, puede obtener visualizaciones agradables utilizando las siguientes herramientas

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

  sudo pip install pycallgraph 

correr:

  pycallgraph mine.py args 

ver:

  gimp pycallgraph.png 

Puedes usar lo que quieras para ver el archivo png, usé gimp
Por desgracia a menudo me pongo

punto: el gráfico es demasiado grande para los mapas de bits de cairo-renderer. Escalado en 0.257079 para encajar

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

 pycallgraph -f svg -o pycallgraph.svg mine.py  

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

 sudo pip install graphviz 

Gráficos alternativos utilizando gprof2dot a través de @maxy / @quodlibetor:

 sudo pip install gprof2dot python -m cProfile -o profile.pstats mine.py gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg 

El comentario de @Maxy sobre esta respuesta me ayudó tanto que creo que merece su propia respuesta: ya tenía archivos .pstats generados con cProfile y no quería volver a ejecutar las cosas con pycallgraph, así que usé gprof2dot , y me puse 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 puntos (lo mismo que usa pycallgraph) para que la salida se vea similar. Aunque tengo la impresión de que gprof2dot pierde menos información:

salida de ejemplo gprof2dot

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

La técnica de visualización principal utilizada es el diagtwig de Sunburst, como se muestra a continuación, en el que la jerarquía de 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 hacer zoom, se puede hacer clic en un arco, y el arco y sus descendientes se ampliarán como un nuevo resplandor solar para mostrar más detalles.

introduzca la descripción de la imagen aquí

Creo que cProfile es excelente para perfilar, mientras que kcachegrind es excelente para visualizar los resultados. El pyprof2calltree entre 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.

También vale la pena mencionar es el visor de volcado de cProfile de GUI RunSnakeRun . Le permite ordenar y seleccionar, por lo tanto, hacer zoom en las partes relevantes del progtwig. El tamaño de los rectangularjs en la imagen es proporcional al tiempo empleado. Si pasas el mouse sobre un rectángulo, resalta esa llamada en la tabla y en todas partes en el mapa. Cuando haces doble clic en un rectángulo, se acerca a esa parte. Le mostrará quién llama a esa parte y cómo llama esa parte.

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. Le indica qué archivo y qué línea para encontrar el código.

También quiero señalar que el OP dijo “perfil”, pero parece que quiso decir “tiempo”. Tenga en cuenta que los progtwigs se ejecutarán más lentamente cuando se los perfile.

introduzca la descripción de la imagen aquí

Un buen módulo de creación de perfiles es el 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. Así que las líneas individuales de código no son cronometradas. Este es un problema en la computación científica, ya que a menudo una sola línea puede llevar mucho tiempo. Además, como recuerdo, cProfile no detectó el tiempo que pasaba en, por ejemplo, numpy.dot.

perfil de perfil

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

Granularidad de línea, analizador de python puro determinista y estadístico sensible al hilo

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

vprof

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

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

mapa de calor

La forma más sencilla y rápida de encontrar el destino de todo el tiempo.

 1. pip install snakeviz 2. python -m cProfile -o temp.dat .py 3. snakeviz temp.dat 

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

Recientemente creé atún para visualizar los perfiles de tiempo de ejecución e importación de Python; Esto puede ser útil aquí.

introduzca la descripción de la imagen aquí

Instalar con

 pip3 install tuna 

Crear un perfil de tiempo de ejecución

 python -mcProfile -o program.prof yourfile.py 

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

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

Luego simplemente ejecuta el atún en el archivo

 tuna program.prof 

Siguiendo la respuesta de Joe Shaw sobre el código de subprocesos múltiples que no funciona como se esperaba, calculé que el método runcall en cProfile es simplemente realizar self.enable() y self.disable() alrededor de la llamada de función perfilada, así que simplemente puedes hacer eso usted mismo y tenga el código que desee en el medio con una mínima interferencia con el código existente.

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

Realmente me olvidé de alguna manera que podría usar en mi IDE (eclipse-PyDev) sin tocar la línea de comandos ni 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.

En la fuente de Virtaal hay una clase y un decorador muy útiles que pueden hacer que el perfilado (incluso para métodos / funciones específicos) sea muy fácil. La salida se puede ver muy cómodamente en KCacheGrind.

cProfile es ideal para la creación rápida de perfiles, pero la mayoría de las veces 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()) 

Mi forma es usar yappi ( https://code.google.com/p/yappi/ ). Es especialmente útil combinado con un servidor RPC donde (incluso solo para la depuración) usted registra el método para iniciar, detener e imprimir información de perfiles, 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 progtwig funcione, puede iniciar el generador de perfiles en cualquier momento llamando al método RPC startProfiler y startProfiler información de creación de perfiles en un archivo de registro llamando a printProfiler (o modificando el método rpc para devolverlo al llamante) y obteniendo 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 .__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 .__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 método printProfiler puede llamarse varias veces a lo largo del tiempo para perfilar y comparar, por ejemplo, diferentes escenarios de uso de progtwigs.

¿Alguna vez has querido saber qué demonios está haciendo ese script de python? Ingrese a la casilla Inspeccionar. Inspeccionar Shell le permite imprimir / alterar globales y ejecutar funciones sin interrumpir el script en ejecución. Ahora con autocompletado 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).

Para agregar a https://stackoverflow.com/a/582337/1070617 ,

Escribí este módulo que le permite usar cProfile y ver su salida fácilmente. Más aquí: https://github.com/ymichael/cprofilev

 $ python -m cprofilev /your/python/program # Go to http://localhost:4000 to view collected statistics. 

También vea: http://ymichael.com/2014/03/08/profiling-python-with-cprofile.html sobre cómo dar sentido a las estadísticas recostackdas.

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

Tiene algunas características únicas como

  • Adjuntar el generador de perfiles a un progtwig en ejecución (CPython)
  • Perfil bajo demanda con integración Yappi.
  • Perfil en una máquina diferente
  • Soporte a múltiples procesos (multiprocesamiento, django …)
  • Muestreo en vivo / vista de la CPU (con selección de rango de tiempo)
  • Perfilado determinístico a través de la integración de perfil / perfil
  • Analizar los 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 de PyCharm

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

Dependería de lo que quieras ver en el perfilado. 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 dadas por cada función y comprender mejor cuánto tiempo se dedica a las funciones, puede usar el perfil c 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, hilos, etc.
Opciones de perfil:
1. line_profiler es otro generador de perfiles que se usa comúnmente para averiguar las métricas de tiempo línea por línea.
2. memory_profiler es una herramienta para perfilar el uso de la memoria.
3. heapy (del proyecto Guppy) Perfil cómo se utilizan los objetos en el montón.

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

También hay un generador de perfiles estadístico 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ínea (no solo basados ​​en funciones). Es más adecuado para aplicaciones suaves en tiempo real como los juegos, pero puede tener menos precisión que cProfile.

La versión en pypi es un poco antigua, por lo que puede instalarla con pip especificando el repository de git :

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

Puedes ejecutarlo así:

 import statprof with statprof.profile(): my_questionable_function() 

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

Cuando no estoy root en el servidor, uso lsprofcalltree.py y ejecuto mi progtwig de esta manera:

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

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