2013-09-23 9 views
5

Próbuję znaleźć źródło nieprzyjemnego wycieku pamięci w programie Python/NumPy przy użyciu rozszerzeń C/Cython i multiprocessing.Debugowanie wycieku pamięci Python/NumPy

Każdy podproces przetwarza listę obrazów, a dla każdego z nich wysyła macierz wyjściową (która zwykle ma wielkość około 200-300 MB) do głównego procesu przez Queue. Dość standardowa mapa/zmniejsz konfigurację.

Jak można sobie wyobrazić, przeciek pamięci może przybrać gigantyczne proporcje z tak dużymi tablicami, a mając wiele procesów z radością przekroczyć 20 GB pamięci RAM, gdy potrzebują tylko 5-6 GB, jest ... denerwujący.

  • Próbowałem uruchomiony debugowania kompilacji Pythona poprzez Valgrind i poczwórną sprawdził moje rozszerzenia dla wycieków pamięci, ale nic nie znalazłem.

  • Sprawdziłem kod Pythona dla wiszących odwołań do moich tablic, a także użyłem NumPy's allocation tracker, aby sprawdzić, czy moje tablice zostały rzeczywiście zwolnione. Byli.

Ostatnią rzeczą, jaką zrobiłem było dołączenie GDB na jeden z moich procesów (ten zły chłopiec jest teraz działa przy 27GB RAM i liczenia) i dumping dużą część hałdy na dysku. Ku mojemu zaskoczeniu, porzucony plik był pełen zer! O wartości zerowej 7G.

Czy jest to standardowe zachowanie przydziału pamięci w Pythonie/NumPy? Czy tęskniłem za czymś oczywistym, co tłumaczyłoby posiadanie tak dużej ilości pamięci na nic? Jak prawidłowo zarządzać pamięcią?


EDIT: Dla przypomnienia, biegnę NumPy 1.7.1 i Python 2.7.3.

EDIT 2: Byłem monitorowanie procesu z strace i wydaje się, że utrzymuje się na zwiększeniu punkt załamania każdego procesu (przy użyciu brk() syscall).

Czy CPython rzeczywiście właściwie zwalnia pamięć? A co z rozszerzeniami C, tablicami NumPy? Kto decyduje, kiedy zadzwonić pod numer brk(), czy jest to sam Python, czy jest to biblioteka bazowa (libc, ...)?

Poniżej znajduje się przykładowy protokół strace z komentarzami, z jednej iteracji (tj. Jeden zestaw obrazów wejściowych). Zauważ, że punkt przerwania stale się zwiększa, ale upewniłem się (z objgraph), że żadne znaczące tablice NumPy nie są przechowywane wewnątrz interpretera Pythona.

# Reading .inf files with metadata 
# Pretty small, no brk() 
open("1_tif_all/AIR00642_1.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 
open("1_tif_all/AIR00642_2.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 
open("1_tif_all/AIR00642_3.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 
open("1_tif_all/AIR00642_4.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 

# This is where I'm starting the heavy processing 
write(2, "[INFO/MapProcess-1] Shot 642: Da"..., 68) = 68 
write(2, "[INFO/MapProcess-1] Shot 642: Vi"..., 103) = 103 
write(2, "[INFO/MapProcess-1] Shot 642: Re"..., 66) = 66 

# I'm opening a .tif image (752 x 480, 8-bit, 1 channel) 
open("1_tif_all/AIR00642_3.tif", O_RDONLY) = 6 
read(6, "II*\0JC\4\0", 8)    = 8 
mmap(NULL, 279600, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fbb000 
munmap(0x7f9387fbb000, 279600)   = 0 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 53) = 53 

# Another .tif 
open("1_tif_all/AIR00642_4.tif", O_RDONLY) = 6 
read(6, "II*\0\266\374\3\0", 8)   = 8 
mmap(NULL, 261532, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fc0000 
munmap(0x7f9387fc0000, 261532)   = 0 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 51) = 51 
brk(0x1aea97000)      = 0x1aea97000 

# Another .tif 
open("1_tif_all/AIR00642_1.tif", O_RDONLY) = 6 
read(6, "II*\0\220\253\4\0", 8)   = 8 
mmap(NULL, 306294, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fb5000 
munmap(0x7f9387fb5000, 306294)   = 0 
brk(0x1af309000)      = 0x1af309000 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 53) = 53 
brk(0x1b03da000)      = 0x1b03da000 

# Another .tif 
open("1_tif_all/AIR00642_2.tif", O_RDONLY) = 6 
mmap(NULL, 345726, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fab000 
munmap(0x7f9387fab000, 345726)   = 0 
brk(0x1b0c42000)      = 0x1b0c42000 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 51) = 51 

# I'm done reading my images 
write(2, "[INFO/MapProcess-1] Shot 642: Fi"..., 72) = 72 

# Allocating some more arrays for additional variables 
# Increases by about 8M at a time 
brk(0x1b1453000)      = 0x1b1453000 
brk(0x1b1c63000)      = 0x1b1c63000 
brk(0x1b2473000)      = 0x1b2473000 
brk(0x1b2c84000)      = 0x1b2c84000 
brk(0x1b3494000)      = 0x1b3494000 
brk(0x1b3ca5000)      = 0x1b3ca5000 

# What are these mmap calls doing here? 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9377df1000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9367be2000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93579d3000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93477c4000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93375b5000 
munmap(0x7f93579d3000, 270594048)  = 0 
munmap(0x7f93477c4000, 270594048)  = 0 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93579d3000 
munmap(0x7f93375b5000, 270594048)  = 0 
mmap(NULL, 50737152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9354970000 
munmap(0x7f9354970000, 50737152)  = 0 
brk(0x1b4cc6000)      = 0x1b4cc6000 
brk(0x1b5ce7000)      = 0x1b5ce7000 

EDIT 3:Is freeing handled differently for small/large numpy arrays? mogą być istotne. Coraz bardziej jestem przekonany, że po prostu przydzielam zbyt wiele tablic, które nie zostaną udostępnione systemowi, ponieważ tak naprawdę jest to standardowe zachowanie. Postaram się wcześniej przydzielić moje tablice i ponownie je wykorzystać w razie potrzeby.

+0

Czego używasz do odczytu plików obrazów? Miałem problemy z wyciekiem pamięci z obiektami PIL 'Image' w przeszłości –

+0

Używam wiązań PyLibTiff. I rozwiązałem to, zobacz moją odpowiedź! –

Odpowiedz

1

Doh. I Naprawdę, powinien sprawdzać rozszerzenia C po raz piąty.

Zapomniałem zmniejszyć licznik referencji w jednej z tymczasowych tablic NumPy przydzielonych z C. Tablica nie zostawiła kodu C, więc nie widziałem, że musiałem zwolnić ją.

Nadal nie mam pojęcia, dlaczego nie pojawił się w objgraph.

Powiązane problemy