Hunting for Memory Leaks in Python applications

Wai Chee Yau

Follow

Feb 13, 2019 – 4 min read

W Zendesk używamy Pythona do budowania produktów uczenia maszynowego (ML). Jednym z powszechnych problemów wydajnościowych, które napotkaliśmy w aplikacjach uczenia maszynowego są wycieki pamięci i spikes. Kod Pythona jest zazwyczaj wykonywany w kontenerach za pomocą frameworków przetwarzania rozproszonego, takich jak Hadoop, Spark i AWS Batch. Każdemu kontenerowi przydzielana jest stała ilość pamięci. Gdy wykonanie kodu przekroczy określony limit pamięci, kontener zostanie zamknięty z powodu błędów braku pamięci.

Szybką poprawką jest zwiększenie przydziału pamięci. Jednak może to spowodować marnotrawstwo zasobów i wpłynąć na stabilność produktów z powodu nieprzewidywalnych skoków pamięci. Przyczyny wycieków pamięci mogą obejmować:

  • lingowanie dużych obiektów, które nie są zwalniane
  • cykle referencyjne w kodzie
  • podstawowe biblioteki/rozszerzenia przeciekające pamięć

Przydatnym ćwiczeniem jest profilowanie użycia pamięci przez aplikacje, aby lepiej zrozumieć wydajność przestrzenną kodu i używanych pakietów bazowych. Ten post obejmuje:

  • profilowanie użycia pamięci w aplikacji w czasie
  • jak sprawdzić użycie pamięci w określonej części programu
  • wskazówki dotyczące debugowania problemów z pamięcią

Możesz spojrzeć na użycie pamięci zmieniające się w czasie podczas wykonywania kodu Pythona używając pakietu memory-profile.

# install the required packages
pip install memory_profiler
pip install matplotlib# run the profiler to record the memory usage
# sample 0.1s by defaut
mprof run --include-children python fantastic_model_building_code.py# plot the recorded memory usage
mprof plot --output memory-profile.png

A. Profil pamięci w funkcji czasu

Opcja include-children powoduje uwzględnienie wykorzystania pamięci przez procesy potomne wywołane przez proces macierzysty. Wykres A przedstawia iteracyjny proces trenowania modelu, który powoduje cykliczny przyrost pamięci w miarę przetwarzania kolejnych partii danych treningowych. Obiekty są uwalniane po rozpoczęciu zbierania śmieci.

Jeśli użycie pamięci stale rośnie, istnieje potencjalny problem wycieków pamięci. Oto przykładowy skrypt, który to zobrazuje.

B. Ślady pamięci rosnące w czasie

Punkt przerwania debugera może być ustawiony, gdy użycie pamięci przekroczy pewien próg przy użyciu opcji pdb-mmem, co jest przydatne przy rozwiązywaniu problemów.

Zrzut pamięci w pewnym momencie

Ważne jest zrozumienie oczekiwanej liczby dużych obiektów w programie i tego, czy powinny one być duplikowane i/lub przekształcane do różnych formatów.

Aby dalej analizować obiekty w pamięci, można utworzyć zrzut sterty podczas pewnych linii kodu w programie za pomocą muppy.

Przykład podsumowania zrzutu sterty pamięci

Inną użyteczną biblioteką do profilowania pamięci jest objgraph, która może generować wykresy obiektów w celu zbadania linii obiektów.

Dążenie do szybkiej pętli sprzężenia zwrotnego

Przydatnym podejściem jest stworzenie małego „przypadku testowego”, który uruchamia tylko kod wycieku pamięci, o którym mowa. Rozważ użycie podzbioru losowo próbkowanych danych, jeśli pełne dane wejściowe są długie do uruchomienia.

Run memory intensive tasks in separate process

Python niekoniecznie zwalnia pamięć natychmiast z powrotem do systemu operacyjnego. Aby mieć pewność, że pamięć jest zwalniana po wykonaniu fragmentu kodu, musi on być uruchamiany w osobnym procesie. Ta strona zawiera więcej szczegółów na temat zbierania śmieci w Pythonie.

Debugger może dodawać referencje do obiektów

Jeśli używany jest debugger z punktem przerwania, taki jak pdb, wszelkie obiekty utworzone i przywołane ręcznie z debuggera pozostaną w profilu pamięci. Może to stworzyć fałszywe poczucie wycieków pamięci, gdzie obiekty nie są zwalniane w odpowiednim czasie.

Uwaga na pakiety, które mogą być nieszczelne

Niektóre biblioteki Pythona mogą potencjalnie mieć wycieki pamięci. Np. pandas ma całkiem sporo znanych problemów z wyciekami pamięci.

Szczęśliwe polowanie!

.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.