J'ai un problème avec mon application python, et je pense que c'est lié à la collection garbage python, même si je ne suis pas sûr ...La collecte des ordures Python peut-elle être aussi lente?
Le problème est que mon application prend beaucoup de temps pour sortir et pour passer à une fonction à la suivante.
Dans mon application, je gère des dictionnaires très volumineux, contenant des milliers de grands objets qui sont instanciés à partir de classes C++ encapsulées. J'ai mis quelques sorties d'horodatage dans mon programme, et j'ai vu qu'à la fin de chaque fonction, quand les objets créés à l'intérieur de la fonction devaient sortir du cadre, l'interprète passe beaucoup de temps avant d'appeler la fonction suivante . Et j'observe le même problème à la fin de l'application, quand le programme devrait sortir: beaucoup de temps (~ heures!) Est passé entre le dernier horodatage à l'écran et l'apparition de l'invite fraîche.
L'utilisation de la mémoire est stable, donc je n'ai pas vraiment de fuites de mémoire.
Des suggestions?
Peut-être la garbage collection de milliers d'objets C++ volumineux qui ralentissent?
Existe-t-il une méthode pour accélérer cela?
MISE À JOUR:
Merci beaucoup pour toutes vos réponses, vous m'a donné beaucoup de conseils pour déboguer mon code :-)
J'utilise Python 2.6.5 scientifiques Linux 5, un Une distribution personnalisée basée sur Red Hat Enterprise 5. En fait, je n'utilise pas SWIG pour obtenir des liaisons Python pour notre code C++, mais le framework Reflex/PyROOT. Je sais, ce n'est pas très connu en dehors de la physique des particules (mais toujours open source et librement disponible) et je dois l'utiliser parce que c'est la valeur par défaut pour notre framework principal.
Et dans ce contexte, la commande DEL du côté Python ne fonctionne pas, je l'avais déjà essayé. DEL supprime uniquement la variable python liée à l'objet C++, pas l'objet lui-même en mémoire, qui appartient toujours au côté C++ ...
... Je sais, ce n'est pas standard, je suppose, et un peu compliqué, désolé :-P
Mais suivant vos conseils, je vais profiler mon code et je reviendrai à vous avec plus de détails, comme vous l'avez suggéré.
Mise à jour supplémentaire:
Ok, suivant vos suggestions, j'instrumenté mon code avec cProfile
, et j'ai découvert que fait la fonction gc.collect()
est la fonction qui prend la plupart du temps en cours d'exécution !!
ici la sortie 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 [...] >>>
Ainsi, dans les deux sorties, triées par « temps » et par le temps « cumulatif » respectivement, gc.collect()
est la fonction de consommer la plupart du temps de fonctionnement de mon programme!: -P
Et c'est la sortie du profileur de mémoire Heapy
, juste avant de retourner le programme 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
Une idée pourquoi ou comment optimiser la garbage collection?
Y a-t-il des vérifications plus détaillées que je peux faire?
"Des suggestions?". Utilisez le profileur pour obtenir plus d'informations sur l'heure de passage. Publiez les résultats en tant que mise à jour de votre question. –
@nos: En fait, Python utilise refcounting, donc un objet non référencé * sera * collecté. Le GC de Python est plutôt simple comparé aux bêtes intelligentes dans les bonnes JVM et dans .NET. – delnan
@delnan pour être précis, l'implémentation de CPython a ce comportement. J'ai l'impression de rappeler quelques versions expérimentales avec des algorithmes beaucoup plus sophistiqués. –