python garbage-collection root performance

La recolección de basura Python puede ser tan lenta?



garbage-collection root (3)

Tengo un problema con mi aplicación de Python, y creo que está relacionado con la colección de basura de Python, incluso si no estoy seguro ...

El problema es que mi aplicación tarda mucho tiempo en salir y cambiar a una función a la siguiente.

En mi aplicación manejo diccionarios muy grandes, que contienen miles de objetos grandes que se crean instancias de clases envueltas de C ++.

Puse algunas salidas de marca de tiempo en mi programa, y ​​vi que al final de cada función, cuando los objetos creados dentro de la función salían del alcance, el intérprete pasaba mucho tiempo antes de llamar a la siguiente función. Y observo el mismo problema al final de la aplicación, cuando el programa debería salir: se pasa mucho tiempo (~ ¡horas!) Entre la última marca de tiempo en la pantalla y la aparición del nuevo aviso.

El uso de memoria es estable, así que realmente no tengo pérdidas de memoria.

¿Alguna sugerencia?

¿Puede ser la recolección de basura de miles de grandes objetos de C ++ que ralentizan?

¿Hay algún método para acelerar eso?

ACTUALIZAR:

Muchas gracias por todas sus respuestas, me dieron muchos consejos para depurar mi código :-)

Utilizo Python 2.6.5 en Scientific Linux 5, una distribución personalizada basada en Red Hat Enterprise 5. Y en realidad no estoy usando SWIG para obtener enlaces Python para nuestro código C ++, sino el marco Reflex / PyROOT. Lo sé, no es muy conocido fuera de la física de partículas (pero sigue siendo de código abierto y de libre acceso) y tengo que usarlo porque es el predeterminado para nuestro marco principal.

Y en este contexto, el comando DEL del lado de Python no funciona, ya lo había intentado. DEL solo elimina la variable python vinculada al objeto C ++, no el objeto en sí en la memoria, que todavía es propiedad del lado C ++ ...

... Lo sé, no es estándar, supongo, y un poco complicado, lo siento :-P

Pero siguiendo tus sugerencias, perfilaré mi código y te responderé con más detalles, como sugeriste.

ACTUALIZACIÓN ADICIONAL:

Ok, siguiendo sus sugerencias, he instrumentado mi código con cProfile , y descubrí que en realidad la función gc.collect() es la función que aprovecha al máximo el tiempo de ejecución.

Aquí el resultado de cProfile + pstats print_stats ():

>>> p.sort_stats("time").print_stats(20) Wed Oct 20 17:46:02 2010 mainProgram.profile 547303 function calls (542629 primitive calls) in 548.060 CPU seconds Ordered by: internal time List reduced from 727 to 20 due to restriction ncalls tottime percall cumtime percall filename:lineno(function) 4 345.701 86.425 345.704 86.426 {gc.collect} 1 167.115 167.115 200.946 200.946 PlotD3PD_v3.2.py:2041(PlotSamplesBranches) 28 12.817 0.458 13.345 0.477 PlotROOTUtils.py:205(SaveItems) 9900 10.425 0.001 10.426 0.001 PlotD3PD_v3.2.py:1973(HistoStyle) 6622 5.188 0.001 5.278 0.001 PlotROOTUtils.py:403(__init__) 57 0.625 0.011 0.625 0.011 {built-in method load} 103 0.625 0.006 0.792 0.008 dbutils.py:41(DeadlockWrap) 14 0.475 0.034 0.475 0.034 {method ''dump'' of ''cPickle.Pickler'' objects} 6622 0.453 0.000 5.908 0.001 PlotROOTUtils.py:421(CreateCanvas) 26455 0.434 0.000 0.508 0.000 /opt/root/lib/ROOT.py:215(__getattr__) [...] >>> p.sort_stats("cumulative").print_stats(20) Wed Oct 20 17:46:02 2010 mainProgram.profile 547303 function calls (542629 primitive calls) in 548.060 CPU seconds Ordered by: cumulative time List reduced from 727 to 20 due to restriction ncalls tottime percall cumtime percall filename:lineno(function) 1 0.001 0.001 548.068 548.068 PlotD3PD_v3.2.py:2492(main) 4 0.000 0.000 346.756 86.689 /usr/lib//lib/python2.5/site-packages/guppy/heapy/Use.py:171(heap) 4 0.005 0.001 346.752 86.688 /usr/lib//lib/python2.5/site-packages/guppy/heapy/View.py:344(heap) 1 0.002 0.002 346.147 346.147 PlotD3PD_v3.2.py:2537(LogAndFinalize) 4 345.701 86.425 345.704 86.426 {gc.collect} 1 167.115 167.115 200.946 200.946 PlotD3PD_v3.2.py:2041(PlotBranches) 28 12.817 0.458 13.345 0.477 PlotROOTUtils.py:205(SaveItems) 9900 10.425 0.001 10.426 0.001 PlotD3PD_v3.2.py:1973(HistoStyle) 13202 0.336 0.000 6.818 0.001 PlotROOTUtils.py:431(PlottingCanvases) 6622 0.453 0.000 5.908 0.001 /root/svn_co/rbianchi/SoftwareDevelopment [...] >>>

Entonces, en ambas salidas, ordenadas por "tiempo" y por tiempo "acumulativo" respectivamente, ¡ gc.collect() es la función que consume la mayor parte del tiempo de ejecución de mi programa! :-PAG

Y esta es la salida del analizador de memoria Heapy , justo antes de devolver el programa main() .

memory usage before return: Partition of a set of 65901 objects. Total size = 4765572 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 25437 39 1452444 30 1452444 30 str 1 6622 10 900592 19 2353036 49 dict of PlotROOTUtils.Canvas 2 109 0 567016 12 2920052 61 dict of module 3 7312 11 280644 6 3200696 67 tuple 4 6622 10 238392 5 3439088 72 0xa4ab74c 5 6622 10 185416 4 3624504 76 PlotROOTUtils.Canvas 6 2024 3 137632 3 3762136 79 types.CodeType 7 263 0 129080 3 3891216 82 dict (no owner) 8 254 0 119024 2 4010240 84 dict of type 9 254 0 109728 2 4119968 86 type Index Count % Size % Cumulative % Kind (class / dict of class) 10 1917 3 107352 2 4264012 88 function 11 3647 5 102116 2 4366128 90 ROOT.MethodProxy 12 148 0 80800 2 4446928 92 dict of class 13 1109 2 39924 1 4486852 93 __builtin__.wrapper_descriptor 14 239 0 23136 0 4509988 93 list 15 87 0 22968 0 4532956 94 dict of guppy.etc.Glue.Interface 16 644 1 20608 0 4553564 94 types.BuiltinFunctionType 17 495 1 19800 0 4573364 94 __builtin__.weakref 18 23 0 11960 0 4585324 95 dict of guppy.etc.Glue.Share 19 367 1 11744 0 4597068 95 __builtin__.method_descriptor

¿Alguna idea de por qué o cómo optimizar la recolección de basura?

¿Hay alguna verificación más detallada que pueda hacer?


Sí, podría ser una recolección de basura, pero también podría ser una sincronización con el código C ++, o algo completamente diferente (difícil de decir sin código).

De todos modos, debería echar un vistazo a SIG para el desarrollo de la integración de Python / C ++ para encontrar problemas y cómo acelerar las cosas.


Si su problema es realmente la recolección de basura, intente liberar explícitamente sus objetos cuando haya terminado con ellos usando del() .

En general, esto no suena como un problema de recolección de basura, a menos que estemos hablando de terabytes de memoria.

Estoy de acuerdo con S.Lott ... perfila tu aplicación, luego traigo fragmentos de código y los resultados de eso y podemos ser mucho más útiles.


Este es un problema conocido del recolector de basura en Python 2.6 que causa un tiempo cuadrático para la recolección de basura cuando se asignan muchos objetos sin desasignar ninguno de ellos, es decir. población de gran lista.
Hay dos soluciones simples:

  1. deshabilitar la recolección de basura antes de completar listas grandes y habilitarla después

    l = [] gc.disable() for x in xrange(10**6): l.append(x) gc.enable()

  2. o actualiza a Python 2.7, donde el problema ha sido resuelto

Prefiero la segunda solución, pero no siempre es una opción;)