2010-10-12 15 views
15

Mam problem z mojej aplikacji Pythona, i myślę, że jest to związane z gromadzeniem śmieci python, nawet jeśli nie jestem pewien ...Zbiór śmieci Pythona może być tak wolny?

Problemem jest to, że moja aplikacja zajmuje dużo czasu, aby wyjść i aby przełączyć się do jednej funkcji na następną.

W mojej aplikacji obsługuję bardzo duże słowniki, zawierające tysiące dużych obiektów, które są tworzone z klas C++.

Umieściłem w moim programie dane wyjściowe sygnatury czasowej i zobaczyłem, że na końcu każdej funkcji, gdy obiekty utworzone wewnątrz funkcji wychodzą poza zakres, tłumacz spędza dużo czasu przed wywołaniem następnej funkcji . I widzę ten sam problem na końcu aplikacji, kiedy program powinien wyjść: dużo czasu (~ godzin!) Spędza się między ostatnim znacznikiem czasu na ekranie a pojawieniem się nowego znaku zachęty.

Zużycie pamięci jest stabilne, więc nie mam przecieków pamięci.

Wszelkie sugestie?

Może być zbiorem śmieci tysięcy dużych obiektów C++, które spowalniają?

Czy istnieje metoda przyspieszenia tego?

UPDATE:

dziękuję za wszystkie odpowiedzi, daliście mi wiele wskazówek do debugowania mojego kodu :-)

używam Python 2.6.5 na Scientific Linux 5, A spersonalizowana dystrybucja oparta na Red Hat Enterprise 5. A tak naprawdę nie używam SWIG, aby uzyskać powiązania Pythona dla naszego kodu C++, ale framework Reflex/PyROOT. Wiem, że nie jest to bardzo znana fizyka cząstek zewnętrznych (ale wciąż jest otwarta i dostępna bezpłatnie) i muszę ją używać, ponieważ jest domyślna dla naszej głównej struktury.

W tym kontekście polecenie DEL ze strony Pythona nie działa, już próbowałem. DEL usuwa tylko zmienną pythonową powiązaną z obiektem C++, a nie sam obiekt w pamięci, który nadal jest własnością strony C++ ...

... Wiem, to nietypowe, jak sądzę, i nieco skomplikowany, przepraszam :-P

Ale podążając za wskazówkami, będę profilować mój kod, a ja wrócę do ciebie z większą ilością szczegółów, zgodnie z sugestią.

DODATKOWE UPDATE:

Ok, po swoje sugestie, że oprzyrządowanie mój kod z cProfile i odkryłem, że rzeczywiście funkcja gc.collect() jest funkcja biorąc większość czasu działa !!

Na wyjściu z 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 

[...] 

>>> 

Tak więc, w obydwu wyjść sortowane przez „czas” i „łączny” czas odpowiednio gc.collect() jest funkcją konsumującym większość czas pracy mojego programu!:-P

I to jest wyjście profilera pamięci Heapy, tuż przed zwróceniem programu 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 

Każdy pomysł, dlaczego lub jak zoptymalizować odbiór śmieci?

Czy mogę przeprowadzić dokładniejszą kontrolę?

+9

"Jakieś sugestie?". Użyj profilera, aby uzyskać więcej informacji o tym, gdzie spędza się czas. Opublikuj wyniki jako aktualizację swojego pytania. –

+0

@nos: Właściwie, Python używa refcountingu, więc obiekt bez referencji * zostanie * zebrany. GC Pythona jest raczej proste w porównaniu do sprytnych bestii w dobrych maszynach JVM i .NET. – delnan

+0

@delnan aby być precyzyjnym, implementacja CPythona ma takie zachowanie. Wydaje mi się, że pamiętam niektóre wersje eksperymentalne z dużo bardziej wyszukanymi algorytmami. –

Odpowiedz

7

This is known garbage collector issue in Python 2.6 powodując kwadratowy czas zbierania śmieci, gdy wiele obiektów jest przydzielanych bez zwolnienia któregoś z nich, tj. populacja dużej listy.
Istnieją dwa proste rozwiązania:

  1. albo wyłączyć zbieranie śmieci przed wypełniania dużych list i włączyć ją potem

    l = [] 
    gc.disable() 
    for x in xrange(10**6): 
        l.append(x) 
    gc.enable() 
    
  2. lub aktualizację Python 2.7, where the issue has been solved

Ja pre drugie rozwiązanie, ale nie zawsze jest to możliwe;)

6

Tak, może to być garbage collection, ale może to być również synchronizacja z kodem C++ lub coś zupełnie innego (trudno powiedzieć bez kodu).

W każdym razie powinieneś rzucić okiem na SIG for development of Python/C++ integration, aby znaleźć problemy i jak przyspieszyć działanie.

+0

Cześć kriss, najwyraźniej ma problem ze śmieciami. Zaktualizowałem swoje pytanie o wynik cProfile i wydaje mi się, że gc.collect pochłania większość czasu pracy ... Spojrzę na SIG Python/C++ zgodnie z sugestią, aby sprawdzić, czy jest to znany problem w niektórych kontekstach. – rmbianchi

+0

raw bet: gc jest prawdopodobnie w przypadku, gdy przeliczanie nie działa dobrze (musi użyć bardziej złożonej strategii odzyskiwania pamięci). Wszelkie pętle w strukturze danych (odniesienia cykliczne)? Jeśli jest źródłem problemu, cięcie ich "ręcznie" może być rozwiązaniem, dzięki czemu gc byłby szczęśliwszy (stąd szybciej). – kriss

-3

Jeśli Twoim problemem jest w rzeczywistości zbieranie śmieci, spróbuj jawnie zwolnić obiekty, gdy skończysz je przy użyciu del().

Generalnie nie brzmi to jak problem ze zbieraniem pamięci, chyba że mówimy o terabajtach pamięci.

Zgadzam się z S.Lott ... profiluj swoją aplikację, a następnie prześlij fragmenty kodu i ich wyniki, a my możemy być bardziej pomocni.

+2

'del' nic nie zwalnia. Po prostu usuwa zmienną z bieżącego zakresu, tj. Usuwa jedno odwołanie. Ale Python jest przeliczany (bardziej wyrafinowany GC istnieje i jest uruchamiany, ale tylko w cyklicznych odniesieniach) - nie ma znaczenia, czy gromada obiektów dostanie się na końcu funkcji lub w małych kawałkach, gdy się młodszy gotowe. – delnan

+0

Ogólnie, tak. W patologicznym przypadku pomocne może być uwolnienie małych kawałków. Zwykle używanie 'del' w każdym miejscu jest oznaką, że nie programujesz w Pythonie. –

+0

Dzięki, Paul i Delnan. Właściwie próbowałem również użyć del(), ale w tym kontekście nie działa. Jak powiedziałem w aktualizacji mojego pytania, używam frameworku open source o nazwie ROOT (http://root.cern.ch), z własnym systemem powiązań Pythona (nazywanym Reflex), a nawet jeśli del() usuwa wszystkie odwołania Pythona do obiektu C++, sam obiekt pozostaje w pamięci ... Ale chociaż znalazłem sposób, aby wyraźnie je usunąć, funkcja gc.collect wydaje się pochłaniać większość czasu działania ... Wszelkie sugestie dotyczące dalszych kontroli? Wielkie dzięki za pomoc – rmbianchi

Powiązane problemy