xticks barplot python performance profiling time-complexity

python - barplot - pandas plot



¿Cómo puedes perfilar un guión? (23)

El proyecto Euler y otros concursos de codificación a menudo tienen un tiempo máximo de ejecución o la gente presume 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 programa python?


¿Alguna vez has querido saber qué demonios está haciendo ese script de python? Entra en el Shell de inspección. 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).


perfil de perfil

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

Granularidad de línea, analizador de pitón puro determinista y estadístico sensible al hilo

Proporciona una granularidad de línea como line_profiler , es 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 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 programa Python, como el tiempo de ejecución y el uso de memoria.


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:


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

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

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


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 para 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 book Es un buen libro para comenzar teniendo en cuenta el rendimiento. Puede pasar a temas avanzados sobre el uso de Python compilado Cython y JIT (Just-in-time).


El comentario de @Maxy sobre esta respuesta me ayudó tanto que creo 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 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:


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 <args>

o archivar:

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

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 adaptarse

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 <args>

PS> asegúrese de instalar graphviz (que proporciona el programa 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


En la source 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.


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

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:


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

Realmente extrañé de alguna manera que pudiera 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.


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 diagrama 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.


Mi forma es usar yappi ( https://code.google.com/p/yappi/ ). Es especialmente útil combinado con un servidor RPC en el que (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 programa 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 modifique el método rpc para devolverlo al printProfiler de la llamada) y obtenga 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 método printProfiler puede llamarse varias veces a lo largo del tiempo para perfilar y comparar, por ejemplo, diferentes escenarios de uso de programas.



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 la determinación de 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 perfil c cuando se 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 <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 .


Recientemente creé tuna para visualizar los perfiles de tiempo de ejecución e importación de Python; Esto puede ser útil 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 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.


También hay un perfilador 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 en tiempo real como los juegos, pero puede tener menos precisión que cProfile.

La statprof es un poco antigua, así que puede instalarla con pip especificando el repositorio 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://.com/a/10333592/320036


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 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 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 programas se ejecutarán más lentamente cuando se los perfile.


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

Mi entendimiento es 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.


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 programa 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 CPU (con selección de rango de tiempo)
  • Perfilado determinista 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.


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 usa. 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.


cProfile es excelente 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())


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 <PROGRAM>.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.