2013-09-23 11 views
5

Ich versuche, den Ursprung eines bösen Speicherverlust in einem Python/NumPy-Programm mit C/Cython-Erweiterungen und multiprocessing zu finden.Debugging eines Python/NumPy Speicherlecks

Jeder Unterprozess verarbeitet eine Liste von Bildern, und für jeden von ihnen sendet das Ausgabe-Array (das normalerweise etwa 200-300MB groß ist) über eine Queue an den Hauptprozess. Ziemlich Standard-Karte/Setup reduzieren.

Wie Sie sich vorstellen können, kann ein Speicherleck ganormous Proportionen mit Arrays so groß nehmen, und mehrere Prozesse glücklich gehen über 20 GB RAM, wenn sie nur 5-6GB benötigen würde ... ist ärgerlich. meine Erweiterungen für Speicherlecks

  • Ich habe versucht, eine Debug-Version von Python durch Valgrind ausgeführt wird, und Quadruple-geprüft, aber nichts gefunden.

  • Ich habe meinen Python-Code überprüft, um Verweise auf meine Arrays zu erzeugen, und habe auch NumPy's allocation tracker verwendet, um zu überprüfen, ob meine Arrays tatsächlich freigegeben wurden. Sie sind.

Das letzte, was ich GDB auf einem meiner Prozesse tat, war aufstecken (das Bad Boy ist bei 27GB RAM und Zählen jetzt ausgeführt) und einen großen Teil des Haufens auf die Platte Dumping. Zu meiner großen Überraschung war die gedumpte Datei voller Nullen! Ungefähr 7G Nullen.

Ist dies ein Standard-Speicherzuordnungsverhalten in Python/NumPy? Habe ich etwas Offensichtliches vermisst, das erklären würde, dass so viel Speicher für nichts genutzt wird? Wie kann ich den Speicher richtig verwalten?


EDIT: Für das Protokoll, ich bin mit NumPy 1.7.1 und Python 2.7.3.

EDIT 2: Ich habe Überwachung des Prozesses mit strace, und es scheint, dass es auf die Erhöhung der Bruchstelle eines jeden Prozesses hält (mit dem brk() syscall).

Gibt CPython tatsächlich Speicher ordnungsgemäß frei? Was ist mit C-Erweiterungen, NumPy-Arrays? Wer entscheidet wann brk(), ist es Python selbst oder ist es eine zugrunde liegende Bibliothek (libc, ...)?

Unten ist ein Beispiel für Strace-Log mit Kommentaren von einer Iteration (d. H. Ein Eingabebildsatz). Beachten Sie, dass der Breakpoint weiter zunimmt, aber ich habe (mit objgraph) sichergestellt, dass keine sinnvollen NumPy-Arrays im Python-Interpreter enthalten sind.

# 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? relevant sein können. Ich bin zunehmend davon überzeugt, dass ich einfach zu viele Arrays verteile, die nicht an das System weitergegeben werden, weil es sich tatsächlich um Standardverhalten handelt. Ich werde versuchen, meine Arrays vorher zuzuweisen und sie bei Bedarf wiederzuverwenden.

+0

Was verwenden Sie zum Lesen der Bilddateien? Ich hatte Speicherverlust Probleme mit PIL 'Image' Objekte in der Vergangenheit –

+0

Ich verwende die PyLibTiff Bindings. Und ich habe das gelöst, sehe meine Antwort! –

Antwort

1

Doh. I wirklich sollte diese C-Erweiterungen ein fünftes Mal überprüft haben.

Ich hatte vergessen, eine Referenzzählung in einem der temporären NumPy-Arrays zu dekrementieren, die ich von C zugewiesen hatte. Das Array hat den C-Code nicht verlassen, also habe ich nicht gesehen, dass ich es freigeben musste.

Ich habe immer noch keine Ahnung, warum es nicht in objgraph erschien.