profile — Pure Python profiler

Source code: Lib/profile.py


Deprecated since version 3.15, will be removed in version 3.17.

The profile module is deprecated and will be removed in Python 3.17. Use profiling.tracing instead.

The profile module provides a pure Python implementation of a deterministic profiler. While useful for understanding profiler internals or extending profiler behavior through subclassing, its pure Python implementation introduces significant overhead compared to the C-based profiling.tracing module.

For most profiling tasks, use:

Migration

Migrating from profile to profiling.tracing is straightforward. The APIs are compatible:

# Old (deprecated)
import profile
profile.run('my_function()')

# New (recommended)
import profiling.tracing
profiling.tracing.run('my_function()')

For most code, replacing import profile with import profiling.tracing (and using profiling.tracing instead of profile throughout) provides a straightforward migration path.

Nota

The cProfile module remains available as a backward-compatible alias to profiling.tracing. Existing code using import cProfile will continue to work without modification.

profile and profiling.tracing module reference

Both the profile and profiling.tracing modules provide the following functions:

profile.run(command, filename=None, sort=-1)

Esta función toma un único argumento que se puede pasar a la función exec() y un nombre de archivo opcional. En todos los casos esta rutina ejecuta:

exec(command, __main__.__dict__, __main__.__dict__)

y recopila estadísticas de perfiles de la ejecución. Si no hay ningún nombre de archivo, esta función crea automáticamente una instancia de Stats e imprime un informe de perfil simple. Si se especifica el valor de clasificación, se pasa a esta instancia Stats para controlar cómo se ordenan los resultados.

profile.runctx(command, globals, locals, filename=None, sort=-1)

This function is similar to run(), with added arguments to supply the globals and locals mappings for the command string. This routine executes:

exec(command, globals, locals)

y recopila estadísticas de perfiles como en la función run() anterior.

class profile.Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True)

This class is normally only used if more precise control over profiling is needed than what the profiling.tracing.run() function provides.

Se puede suministrar un temporizador personalizado para medir cuánto tiempo tarda el código en ejecutarse mediante el argumento timer. Esta debe ser una función que retorna un solo número que representa la hora actual. Si el número es un entero, la timeunit especifica un multiplicador que especifica la duración de cada unidad de tiempo. Por ejemplo, si el temporizador retorna tiempos medidos en miles de segundos, la unidad de tiempo sería .001.

El uso directo de la clase Profile permite formatear los resultados del perfil sin escribir los datos del perfil en un archivo:

import profiling.tracing
import pstats
import io
from pstats import SortKey

pr = profiling.tracing.Profile()
pr.enable()
# ... do something ...
pr.disable()
s = io.StringIO()
sortby = SortKey.CUMULATIVE
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print(s.getvalue())

The Profile class can also be used as a context manager (supported only in profiling.tracing, not in the deprecated profile module; see Tipos gestores de contexto):

import profiling.tracing

with profiling.tracing.Profile() as pr:
    # ... do something ...

    pr.print_stats()

Distinto en la versión 3.8: Se agregó soporte de administrador de contexto.

enable()

Start collecting profiling data. Only in profiling.tracing.

disable()

Stop collecting profiling data. Only in profiling.tracing.

create_stats()

Deja de recopilar datos de perfiles y registre los resultados internamente como el perfil actual.

print_stats(sort=-1)

Crea un objeto Stats en función del perfil actual e imprima los resultados en stdout.

The sort parameter specifies the sorting order of the displayed statistics. It accepts a single key or a tuple of keys to enable multi-level sorting, as in pstats.Stats.sort_stats().

Added in version 3.13: print_stats() now accepts a tuple of keys.

dump_stats(filename)

Escribe los resultados del perfil actual en filename.

run(cmd)

Perfila el cmd a través de exec().

runctx(cmd, globals, locals)

Perfila el cmd a través de exec() con el entorno global y local especificado.

runcall(func, /, *args, **kwargs)

Perfila func(*args, **kwargs)

Tenga en cuenta que la creación de perfiles solo funcionará si el comando/función llamado realmente regresa. Si el intérprete se termina (por ejemplo, a través de una llamada a sys.exit() durante la ejecución del comando/función llamado) no se imprimirán resultados de generación de perfiles.

Differences from profiling.tracing

The profile module differs from profiling.tracing in several ways:

Higher overhead. The pure Python implementation is significantly slower than the C implementation, making it unsuitable for profiling long-running programs or performance-sensitive code.

Calibration support. The profile module supports calibration to compensate for profiling overhead. This is not needed in profiling.tracing because the C implementation has negligible overhead.

Custom timers. Both modules support custom timers, but profile accepts timer functions that return tuples (like os.times()), while profiling.tracing requires a function returning a single number.

Subclassing. The pure Python implementation is easier to subclass and extend for custom profiling behavior.

What is deterministic profiling?

Deterministic profiling is meant to reflect the fact that all function call, function return, and exception events are monitored, and precise timings are made for the intervals between these events (during which time the user’s code is executing). In contrast, statistical profiling (which is provided by the profiling.sampling module) periodically samples the effective instruction pointer, and deduces where time is being spent. The latter technique traditionally involves less overhead (as the code does not need to be instrumented), but provides only relative indications of where time is being spent.

En Python, dado que hay un intérprete activo durante la ejecución, no se requiere la presencia de código instrumentado para realizar un perfil determinista. Python proporciona automáticamente un hook (retrollamada opcional) para cada evento. Además, la naturaleza interpretada de Python tiende a agregar tanta sobrecarga a la ejecución, que los perfiles deterministas tienden a agregar solo una pequeña sobrecarga de procesamiento en aplicaciones típicas. El resultado es que el perfil determinista no es tan costoso, pero proporciona estadísticas extensas de tiempo de ejecución sobre la ejecución de un programa Python.

Las estadísticas de recuento de llamadas se pueden usar para identificar errores en el código (recuentos sorprendentes) e identificar posibles puntos de expansión en línea (recuentos altos de llamadas). Las estadísticas de tiempo interno se pueden utilizar para identificar «bucles activos» que deben optimizarse cuidadosamente. Las estadísticas de tiempo acumulativo deben usarse para identificar errores de alto nivel en la selección de algoritmos. Tenga en cuenta que el manejo inusual de los tiempos acumulativos en este generador de perfiles permite que las estadísticas de implementaciones recursivas de algoritmos se comparen directamente con implementaciones iterativas.

Limitaciones

Una limitación tiene que ver con la precisión de la información de sincronización. Hay un problema fundamental con los perfiladores deterministas que implican precisión. La restricción más obvia es que el «reloj» subyacente solo funciona a una velocidad (típicamente) de aproximadamente 0,001 segundos. Por lo tanto, ninguna medición será más precisa que el reloj subyacente. Si se toman suficientes medidas, entonces el «error» tenderá a promediar. Desafortunadamente, eliminar este primer error induce una segunda fuente de error.

El segundo problema es que «lleva un tiempo» desde que se distribuye un evento hasta que la llamada del generador de perfiles para obtener la hora en realidad obtiene el estado del reloj. Del mismo modo, hay un cierto retraso al salir del controlador de eventos del generador de perfiles desde el momento en que se obtuvo el valor del reloj (y luego se retiró), hasta que el código del usuario se está ejecutando nuevamente. Como resultado, las funciones que se llaman muchas veces, o llaman a muchas funciones, generalmente acumularán este error. El error que se acumula de esta manera es típicamente menor que la precisión del reloj (menos de un tic de reloj), pero puede acumularse y volverse muy significativo.

The problem is more important with the deprecated profile module than with the lower-overhead profiling.tracing. For this reason, profile provides a means of calibrating itself for a given platform so that this error can be probabilistically (on the average) removed. After the profiler is calibrated, it will be more accurate (in a least square sense), but it will sometimes produce negative numbers (when call counts are exceptionally low, and the gods of probability work against you :-). ) Do not be alarmed by negative numbers in the profile. They should only appear if you have calibrated your profiler, and the results are actually better than without calibration.

Calibración

El generador de perfiles del módulo profile resta una constante de cada tiempo de manejo de eventos para compensar la sobrecarga de llamar a la función de tiempo y eliminar los resultados. Por defecto, la constante es 0. El siguiente procedimiento se puede usar para obtener una mejor constante para una plataforma dada (ver Limitaciones).

import profile
pr = profile.Profile()
for i in range(5):
    print(pr.calibrate(10000))

El método ejecuta la cantidad de llamadas de Python dadas por el argumento, directamente y nuevamente bajo el generador de perfiles, midiendo el tiempo para ambos. Luego, calcula la sobrecarga oculta por evento del generador de perfiles y la retorna como un valor flotante. Por ejemplo, en un Intel Core i5 de 1.8Ghz que ejecuta macOS y usa el time.process_time () de Python como temporizador, el número mágico es aproximadamente 4.04e-6.

El objetivo de este ejercicio es obtener un resultado bastante consistente. Si su computadora es muy rápida, o su función de temporizador tiene una resolución pobre, es posible que tenga que pasar 100000, o incluso 1000000, para obtener resultados consistentes.

Cuando tiene una respuesta consistente, hay tres formas de usarla:

import profile

# 1. Apply computed bias to all Profile instances created hereafter.
profile.Profile.bias = your_computed_bias

# 2. Apply computed bias to a specific Profile instance.
pr = profile.Profile()
pr.bias = your_computed_bias

# 3. Specify computed bias in instance constructor.
pr = profile.Profile(bias=your_computed_bias)

Si tiene una opción, es mejor que elija una constante más pequeña, y luego sus resultados «con menos frecuencia» se mostrarán como negativos en las estadísticas del perfil.

Usando un temporizador personalizado

Si desea cambiar la forma en que se determina el tiempo actual (por ejemplo, para forzar el uso del tiempo del reloj de pared o el tiempo transcurrido del proceso), pase la función de temporización que desee a la clase constructora Profile:

pr = profile.Profile(your_time_func)

The resulting profiler will then call your_time_func. Depending on whether you are using profile.Profile or profiling.tracing.Profile, your_time_func’s return value will be interpreted differently:

profile.Profile

your_time_func debería retornar un solo número o una lista de números cuya suma es la hora actual (como lo que os.times() retorna). Si la función retorna un número de tiempo único o la lista de números retornados tiene una longitud de 2, obtendrá una versión especialmente rápida de la rutina de envío.

Be warned that you should calibrate the profiler class for the timer function that you choose (see Calibración). For most machines, a timer that returns a lone integer value will provide the best results in terms of low overhead during profiling. (os.times() is pretty bad, as it returns a tuple of floating-point values). If you want to substitute a better timer in the cleanest fashion, derive a class and hardwire a replacement dispatch method that best handles your timer call, along with the appropriate calibration constant.

profiling.tracing.Profile

your_time_func debería retornar un solo número. Si retorna enteros, también puede invocar al constructor de la clase con un segundo argumento que especifique la duración real de una unidad de tiempo. Por ejemplo, si your_integer_time_func retorna tiempos medidos en miles de segundos, construiría la instancia Profile de la siguiente manera:

pr = profiling.tracing.Profile(your_integer_time_func, 0.001)

As the profiling.tracing.Profile class cannot be calibrated, custom timer functions should be used with care and should be as fast as possible. For the best results with a custom timer, it might be necessary to hard-code it in the C source of the internal _lsprof module.

Python 3.3 agrega varias funciones nuevas en time que se puede usar para realizar mediciones precisas del proceso o el tiempo del reloj de pared (wall-clock time). Por ejemplo, vea time.perf_counter().

Ver también

profiling

Overview of Python profiling tools.

profiling.tracing

Recommended replacement for this module.

pstats

Statistical analysis and formatting for profile data.