Por lo general, uso el comando de shell time
. Mi propósito es probar si los datos son conjuntos pequeños, medianos, grandes o muy grandes, cuánto tiempo y uso de memoria será.
¿Alguna herramienta para Linux o simplemente Python para hacer esto?
Respuestas:
Eche un vistazo a timeit , el generador de perfiles de python y pycallgraph . También asegúrese de echar un vistazo al comentario a continuaciónnikicc
mencionando " SnakeViz ". Le brinda otra visualización de los datos de creación de perfiles que puede ser útil.
def test():
"""Stupid test function"""
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test"))
# For Python>=3.5 one can also write:
print(timeit.timeit("test()", globals=locals()))
Esencialmente, puede pasarle código Python como un parámetro de cadena, y se ejecutará en la cantidad de veces especificada e imprimirá el tiempo de ejecución. Los bits importantes de los documentos :
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
Cree unaTimer
instancia con la declaración dada, el código de configuración y la función de temporizador y ejecute sutimeit
método con ejecuciones numéricas . El argumento global opcional especifica un espacio de nombres en el que ejecutar el código.
... y:
Timer.timeit(number=1000000)
Número de ejecuciones de tiempo de la declaración principal. Esto ejecuta la instrucción de configuración una vez y luego devuelve el tiempo que lleva ejecutar la instrucción principal varias veces, medido en segundos como un valor flotante. El argumento es el número de veces que se recorre el ciclo, con un valor predeterminado de un millón. La declaración principal, la declaración de configuración y la función de temporizador que se utilizarán se pasan al constructor.Nota: De forma predeterminada,
timeit
se apaga temporalmentegarbage collection
durante el tiempo. La ventaja de este enfoque es que hace que los tiempos independientes sean más comparables. Esta desventaja es que GC puede ser un componente importante del desempeño de la función que se mide. Si es así, GC se puede volver a habilitar como la primera declaración en la cadena de configuración . Por ejemplo:
timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()
La creación de perfiles le dará una idea mucho más detallada sobre lo que está sucediendo. Aquí está el "ejemplo instantáneo" de los documentos oficiales :
import cProfile
import re
cProfile.run('re.compile("foo|bar")')
Que te dará:
197 function calls (192 primitive calls) in 0.002 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
1 0.000 0.000 0.001 0.001 re.py:212(compile)
1 0.000 0.000 0.001 0.001 re.py:268(_compile)
1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset)
1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset)
4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction)
3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile)
Ambos módulos deberían darle una idea de dónde buscar los cuellos de botella.
Además, para familiarizarse con el resultado de profile
, eche un vistazo a esta publicación
NOTA pycallgraph ha sido oficialmente abandonado desde febrero de 2018 . Sin embargo, en diciembre de 2020 todavía estaba funcionando en Python 3.6. Sin embargo, siempre que no haya cambios fundamentales en la forma en que Python expone la API de creación de perfiles, debería seguir siendo una herramienta útil.
Este módulo utiliza graphviz para crear gráficos de llamadas como los siguientes:
Puede ver fácilmente qué caminos se utilizaron más tiempo por color. Puede crearlos usando la API de pycallgraph o usando un script empaquetado:
pycallgraph graphviz -- ./mypythonscript.py
Sin embargo, la sobrecarga es bastante considerable. Por lo tanto, para procesos que ya se ejecutan durante mucho tiempo, la creación del gráfico puede llevar algún tiempo.
Utilizo un decorador simple para cronometrar la función.
def st_time(func):
"""
st decorator to calculate the total time of a func
"""
def st_func(*args, **keyArgs):
t1 = time.time()
r = func(*args, **keyArgs)
t2 = time.time()
print "Function=%s, Time=%s" % (func.__name__, t2 - t1)
return r
return st_func
El timeit
módulo era lento y extraño, así que escribí esto:
def timereps(reps, func):
from time import time
start = time()
for i in range(0, reps):
func()
end = time()
return (end - start) / reps
Ejemplo:
import os
listdir_time = timereps(10000, lambda: os.listdir('/'))
print "python can do %d os.listdir('/') per second" % (1 / listdir_time)
Para mi, dice:
python can do 40925 os.listdir('/') per second
Este es un tipo primitivo de evaluación comparativa, pero es suficientemente bueno.
Normalmente hago un rápido time ./script.py
para ver cuánto tiempo lleva. Sin embargo, eso no le muestra la memoria, al menos no por defecto. Puede utilizarlo /usr/bin/time -v ./script.py
para obtener mucha información, incluido el uso de memoria.
/usr/bin/time
con -v
opción no está disponible por defecto en muchas distribuciones, tiene que ser instalado. sudo apt-get install time
en debian, ubuntu, etc. pacman -S time
archlinux
Memory Profiler para todas sus necesidades de memoria.
https://pypi.python.org/pypi/memory_profiler
Ejecute una instalación de pip:
pip install memory_profiler
Importar la biblioteca:
import memory_profiler
Agrega un decorador al artículo que deseas perfilar:
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
Ejecute el código:
python -m memory_profiler example.py
Reciba la salida:
Line # Mem usage Increment Line Contents
==============================================
3 @profile
4 5.97 MB 0.00 MB def my_func():
5 13.61 MB 7.64 MB a = [1] * (10 ** 6)
6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7)
7 13.61 MB -152.59 MB del b
8 13.61 MB 0.00 MB return a
Los ejemplos son de los documentos, vinculados arriba.
Eche un vistazo a nose y a uno de sus complementos, este en particular.
Una vez instalado, nose es un script en su ruta, y al que puede llamar en un directorio que contiene algunos scripts de Python:
$: nosetests
Esto buscará en todos los archivos de Python en el directorio actual y ejecutará cualquier función que reconozca como prueba: por ejemplo, reconoce cualquier función con la palabra test_ en su nombre como prueba.
Entonces puede crear un script de Python llamado test_yourfunction.py y escribir algo como esto en él:
$: cat > test_yourfunction.py
def test_smallinput():
yourfunction(smallinput)
def test_mediuminput():
yourfunction(mediuminput)
def test_largeinput():
yourfunction(largeinput)
Entonces tienes que correr
$: nosetest --with-profile --profile-stats-file yourstatsprofile.prof testyourfunction.py
y para leer el archivo de perfil, use esta línea de Python:
python -c "import hotshot.stats ; stats = hotshot.stats.load('yourstatsprofile.prof') ; stats.sort_stats('time', 'calls') ; stats.print_stats(200)"
nose
basa en hotshot. Ya no se mantiene desde Python 2.5 y solo se mantiene "para uso especializado"
Tenga cuidado, timeit
es muy lento, se necesitan 12 segundos en mi procesador medio para inicializar (o tal vez ejecutar la función). puedes probar esta respuesta aceptada
def test():
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test")) # 12 second
por simple cosa que usaré en su time
lugar, en mi PC devuelve el resultado0.0
import time
def test():
lst = []
for i in range(100):
lst.append(i)
t1 = time.time()
test()
result = time.time() - t1
print(result) # 0.000000xxxx
timeit
ejecuta su función muchas veces, para promediar el ruido. El número de repeticiones es una opción, consulte Tiempos de ejecución de evaluación comparativa en python o la última parte de la respuesta aceptada a esta pregunta.
snakeviz
visor interactivo para cProfile
https://github.com/jiffyclub/snakeviz/
cProfile se mencionó en https://stackoverflow.com/a/1593034/895245 y snakeviz se mencionó en un comentario , pero quería resaltarlo más.
Es muy difícil depurar el rendimiento del programa con solo mirar cprofile
/pstats
output, porque solo pueden totalizar los tiempos por función desde el primer momento.
Sin embargo, lo que realmente necesitamos en general es ver una vista anidada que contenga los seguimientos de la pila de cada llamada para encontrar los principales cuellos de botella fácilmente.
Y esto es exactamente lo que ofrece snakeviz a través de su vista "carámbano" predeterminada.
Primero tiene que volcar los datos de cProfile en un archivo binario, y luego puede serpentear en eso
pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof
Esto imprime una URL a stdout que puede abrir en su navegador, que contiene la salida deseada que se ve así:
y luego puedes:
Pregunta más orientada al perfil: ¿Cómo se puede crear un perfil de un script de Python?
Si no desea escribir código repetitivo para timeit y obtener resultados fáciles de analizar, eche un vistazo a benchmarkit . También guarda el historial de ejecuciones anteriores, por lo que es fácil comparar la misma función durante el transcurso del desarrollo.
# pip install benchmarkit
from benchmarkit import benchmark, benchmark_run
N = 10000
seq_list = list(range(N))
seq_set = set(range(N))
SAVE_PATH = '/tmp/benchmark_time.jsonl'
@benchmark(num_iters=100, save_params=True)
def search_in_list(num_items=N):
return num_items - 1 in seq_list
@benchmark(num_iters=100, save_params=True)
def search_in_set(num_items=N):
return num_items - 1 in seq_set
benchmark_results = benchmark_run(
[search_in_list, search_in_set],
SAVE_PATH,
comment='initial benchmark search',
)
Imprime en el terminal y devuelve la lista de diccionarios con los datos de la última ejecución. Los puntos de entrada de la línea de comandos también están disponibles.
Si cambia N=1000000
y vuelve a ejecutar
python -m cProfile -o results.prof myscript.py
. El archivo oputput puede ser muy bien presentado en un navegador por medio de un programa llamado SnakeViz usandosnakeviz results.prof