Napisałem program w języku Python, który działa na dużym pliku wejściowym, tworząc kilka milionów obiektów reprezentujących trójkąty. Algorytm to:
- odczytać plik wejściowy
- przetworzyć plik i utworzyć listę trójkątów reprezentowanych przez ich wierzchołki
- wypisz wierzchołki w formacie OFF: lista wierzchołków, a następnie lista trójkątów. Trójkąty są reprezentowane przez indeksy na liście wierzchołków
Wymóg WYŁ., Aby wydrukować pełną listę wierzchołków przed wydrukowaniem trójkątów, oznacza, że muszę zapisać listę trójkątów w pamięci przed zapisaniem danych wyjściowych do pliku. W międzyczasie dostaję błędy pamięci z powodu rozmiarów list.
Jak najlepiej powiedzieć Pythonowi, że nie potrzebuję już niektórych danych i że można je uwolnić?
python
memory
memory-management
Nathan Fellman
źródło
źródło
Odpowiedzi:
Zgodnie z oficjalną dokumentacją Pythona możesz zmusić Garbage Collector do zwolnienia pamięci, do której się nie odwołuje
gc.collect()
. Przykład:źródło
gc.collect()
się na końcu pętli może pomóc uniknąć fragmentacji pamięci, co z kolei pomaga zwiększyć wydajność. Widziałem, że robi to znaczącą różnicę (~ 20% czasu działania IIRC)gc.collect()
po załadowaniu dataframe pandy z hdf5 (500k wierszy) Zmniejszone zużycie pamięci z 1.7GB do 500MBdel my_array
po którym następujegc.collect()
po przetworzeniu tablicy jest jedynym sposobem, w jaki pamięć jest faktycznie zwalniana, a mój proces przetrwa, aby załadować następną tablicę.Niestety (w zależności od wersji i wydania Pythona) niektóre typy obiektów używają „wolnych list”, które są zgrabną lokalną optymalizacją, ale mogą powodować fragmentację pamięci, w szczególności przez zwiększanie ilości „pamięci” przeznaczonej tylko na obiekty określonego typu i przez to niedostępny dla „funduszu ogólnego”.
Jedynym naprawdę niezawodnym sposobem, aby zapewnić, że duże, ale tymczasowe użycie pamięci ZRODZIE wszystkie zasoby do systemu po zakończeniu, jest użycie tego w podprocesie, co powoduje, że praca wymagająca dużej ilości pamięci kończy się. W takich warunkach system operacyjny wykona swoje zadanie i chętnie zutylizuje wszystkie zasoby, które podproces mógł pochłonąć. Na szczęście
multiprocessing
moduł sprawia, że tego rodzaju operacje (które kiedyś były raczej uciążliwe) nie są takie złe w nowoczesnych wersjach Pythona.W twoim przypadku wydaje się, że najlepszym sposobem, aby podprocesy zgromadziły niektóre wyniki, a jednocześnie upewnić się, że wyniki te są dostępne dla głównego procesu, jest użycie plików tymczasowych (przez okres przejściowy mam na myśli, NIE rodzaj plików, które automatycznie znikają po zamknięciu, zwykłe pliki, które jawnie usuwasz, gdy już z nimi skończysz).
źródło
multiprocessing.Manager
pliki zamiast implementować stan współdzielony.del
Zestawienie może być przydatne, ale IIRC to nie jest gwarantowane, aby zwolnić pamięć . Dokumenty są tutaj ... i dlaczego nie został wydany jest tutaj .Słyszałem, jak ludzie w systemach Linux i Unix rozpakowują proces Pythona, aby wykonać jakąś pracę, uzyskiwać wyniki, a następnie zabijać.
W tym artykule znajdują się uwagi na temat modułu śmieciowego Pythona, ale myślę, że brak kontroli pamięci jest wadą pamięci zarządzanej
źródło
Python jest zbierany w pamięci, więc jeśli zmniejszysz rozmiar listy, odzyska pamięć. Możesz także użyć instrukcji „del”, aby całkowicie pozbyć się zmiennej:
źródło
Nie można jawnie zwolnić pamięci. Musisz upewnić się, że nie przechowujesz odniesień do obiektów. Będą następnie zbierane śmieci, uwalniając pamięć.
W twoim przypadku, gdy potrzebujesz dużych list, zwykle musisz zreorganizować kod, zwykle za pomocą generatorów / iteratorów. W ten sposób nie musisz w ogóle mieć dużych list w pamięci.
http://www.prasannatech.net/2009/07/introduction-python-generators.html
źródło
(
del
może być twoim przyjacielem, ponieważ zaznacza obiekty jako możliwe do usunięcia, gdy nie ma innych odniesień do nich. Teraz często interpreter CPython zachowuje tę pamięć do późniejszego wykorzystania, więc twój system operacyjny może nie widzieć pamięci „zwolnionej”).Być może nie napotkasz żadnego problemu z pamięcią, używając bardziej zwartej struktury danych. Zatem listy liczb są znacznie mniej wydajne pod względem pamięci niż format używany przez
array
moduł standardowy lubnumpy
moduł innej firmy . Zaoszczędziłbyś pamięć, umieszczając swoje wierzchołki w tablicy NumPy 3xN, a trójkąty w tablicy N-elementowej.źródło
del
nie robi niczego, co po prostu nie przypisałoby innej wartości do wszystkich nazw odwołujących się do obiektu.del
uwalnia pamięć z punktu widzenia Pythona, ale ogólnie nie z punktu widzenia biblioteki wykonawczej C lub systemu operacyjnego. Odnośniki: stackoverflow.com/a/32167625/4297 , effbot.org/pyfaq/… .del
jest równie skuteczny w przypadku wyjść poza zakres, przeniesień itp.Miałem podobny problem z odczytem wykresu z pliku. Przetwarzanie obejmowało obliczenie macierzy zmiennoprzecinkowej 200 000 x 200 000 (jedna linia na raz), która nie mieściła się w pamięci. Próba zwolnienia pamięci między obliczeniami za pomocą
gc.collect()
naprawiła aspekt problemu związany z pamięcią, ale spowodowało to problemy z wydajnością: nie wiem dlaczego, ale chociaż ilość używanej pamięci pozostała stała, każde nowe wywołaniegc.collect()
trwało dłużej niż Poprzedni. Tak szybko gromadzenie śmieci zajęło większość czasu obliczeń.Aby rozwiązać zarówno problemy z pamięcią, jak i wydajnością, przełączyłem się na sztuczkę wielowątkową, którą gdzieś przeczytałem (przepraszam, nie mogę już znaleźć powiązanego postu). Zanim czytałem każdą linię pliku w dużej
for
pętli, przetwarzałem ją igc.collect()
co jakiś czas działałem w celu zwolnienia miejsca w pamięci. Teraz wywołuję funkcję, która odczytuje i przetwarza fragment pliku w nowym wątku. Po zakończeniu wątku pamięć jest automatycznie zwalniana bez dziwnego problemu z wydajnością.Praktycznie działa tak:
źródło
Inni podali pewne sposoby na „nakłonienie” interpretera Pythona do zwolnienia pamięci (lub w inny sposób uniknięcia problemów z pamięcią). Możliwe, że powinieneś najpierw wypróbować ich pomysły. Uważam jednak, że ważne jest udzielenie bezpośredniej odpowiedzi na twoje pytanie.
Tak naprawdę nie ma sposobu, aby bezpośrednio powiedzieć Pythonowi, aby zwolnił pamięć. Faktem jest, że jeśli chcesz mieć tak niski poziom kontroli, będziesz musiał napisać rozszerzenie w C lub C ++.
To powiedziawszy, istnieje kilka narzędzi, które mogą w tym pomóc:
źródło
Jeśli nie obchodzi Cię ponowne użycie wierzchołków, możesz mieć dwa pliki wyjściowe - jeden dla wierzchołków i jeden dla trójkątów. Następnie po zakończeniu dołącz plik trójkąta do pliku wierzchołka.
źródło