Vi använder Python en hel del på Zendesk för att bygga produkter för maskininlärning (ML). Ett av de vanligaste prestandaproblemen som vi stött på med applikationer för maskininlärning är minnesläckage och spikar. Pythonkoden exekveras vanligtvis i behållare via ramverk för distribuerad bearbetning som Hadoop, Spark och AWS Batch. Varje container tilldelas en fast mängd minne. När kodutförandet överskrider den angivna minnesgränsen avslutas behållaren på grund av minnesfel.
En snabb lösning är att öka minnesallokeringen. Detta kan dock leda till slöseri med resurser och påverka produkternas stabilitet på grund av oförutsägbara minnespikar. Orsakerna till minnesläckage kan vara:
- ljudning av stora objekt som inte släpps
- referenscykler i koden
- underliggande bibliotek/C-förlängningar som läcker minne
En användbar övning är att profilera programmens minnesanvändning för att få bättre förståelse för kodens utrymmeseffektivitet och de underliggande paket som används. Det här inlägget täcker:
- Profilering av programmets minnesanvändning över tiden
- Hur man inspekterar minnesanvändningen vid en specifik del av programmet
- Tips för felsökning av minnesproblem
Du kan titta på minnesanvändningen som varierar över tiden under utförandet av Pythonkoden med hjälp av paketet 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
Valet include-children kommer att inkludera minnesanvändningen för alla barnprocesser som skapas via den överordnade processen. Diagram A visar en iterativ modellträningsprocess som gör att minnet ökar i cykler i takt med att partier av träningsdata behandlas. Objekten släpps när skräpplockningen startar.
Om minnesanvändningen ständigt ökar finns det ett potentiellt problem med minnesläckage. Här är ett dummyprovskript för att illustrera detta.
En brytpunkt i felsökaren kan sättas när minnesanvändningen överskrider ett visst tröskelvärde med alternativet pdb-mmem, vilket är praktiskt vid felsökning.
Minnesdump vid en tidpunkt
Det är viktigt att förstå det förväntade antalet stora objekt i programmet och om de bör dupliceras och/eller omvandlas till olika format.
För att ytterligare analysera objekten i minnet kan en heapdump skapas under vissa rader av koden i programmet med muppy.
Ett annat användbart bibliotek för minnesprofilering är objgraph som kan generera objektgrafer för att inspektera objektens härkomst.
Sträva efter snabb återkopplingsslinga
En användbar metod är att skapa ett litet ”testfall” som endast kör den aktuella koden för minnesläckage. Överväg att använda en delmängd av de slumpmässigt samplade uppgifterna om det tar lång tid att köra alla indata.
Kör minnesintensiva uppgifter i en separat process
Python släpper inte nödvändigtvis minnet omedelbart tillbaka till operativsystemet. För att säkerställa att minnet frigörs efter att en kod har utförts måste den köras i en separat process. På den här sidan finns mer information om Python garbage collection.
Debugger kan lägga till referenser till objekt
Om en brytpunktsdebugger som t.ex. pdb används kommer alla objekt som skapats och refereras manuellt från debuggern att finnas kvar i minnesprofilen. Detta kan skapa en falsk känsla av minnesläckor där objekt inte släpps i tid.
Se upp för paket som kan vara läckande
Vissa Pythonbibliotek kan potentiellt ha minnesläckor. Pandas har t.ex. en hel del kända problem med minnesläckage.
Glad jakt!