Я пытаюсь найти происхождение неприятной утечки памяти в программе Python/NumPy с использованием расширений C/Cython и multiprocessing
.Отладка утечки памяти Python/NumPy
Каждый подпроцесс обрабатывает список изображений, и каждый из них отправляет выходной массив (который обычно составляет около 200-300 МБ) через Queue
в основной процесс. Достаточно стандартная карта/сокращение настроек.
Как вы можете себе представить, утечка памяти может принимать большие размеры с массивами, которые являются большими, и с несколькими процессами с радостью переходят на 20 ГБ оперативной памяти, когда им понадобится только 5-6 ГБ ... раздражает.
Я попытался запустить отладки сборки Python через Valgrind и четверной проверил мои расширения утечек памяти, но ничего не нашли.
Я проверил свой код на Python для зависания ссылок на мои массивы, а также использовал NumPy allocation tracker, чтобы проверить, действительно ли мои массивы были выпущены. Они были.
Последнее, что я сделал, это прикрепление GDB на один из моих процессов (это плохой мальчик сейчас работает на 27GB оперативной памяти и подсчета голосов) и демпинг большую часть кучи на диск. К моему большому удивлению, сброшенный файл был полон нулей! Около 7G стоит нулей.
Является ли это стандартным положением распределения памяти в Python/NumPy? Я пропустил что-то очевидное, что объясняло бы, что столько памяти использовалось для ничего? Как я могу правильно управлять памятью?
EDIT: Для записи, я бегу NumPy 1.7.1 и Python 2.7.3.
EDIT 2: Я контролировала процесс с strace
, и кажется, что она продолжает увеличивать точку останова каждого процесса (с использованием brk()
системного вызова).
Действительно ли CPython правильно освобождает память? Что относительно расширений C, массивов NumPy? Кто решает, когда позвонить brk()
, это сам Python или это базовая библиотека (libc
, ...)?
Ниже приведен пример журнала strace с комментариями, начиная с одной итерации (то есть с одного набора входных изображений). Обратите внимание, что точка останова продолжает расти, но я убедился (с objgraph
), что никаких значимых массивов NumPy не содержится внутри интерпретатора Python.
# 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? может быть уместным. Я все больше убеждаюсь, что я просто выделяю слишком много массивов, которые не попадают в систему, потому что это действительно стандартное поведение. Постарайтесь заранее распределить мои массивы и использовать их по мере необходимости.
Что вы используете для чтения файлов изображений? У меня были проблемы с утечкой памяти с объектами PIL 'Image' в прошлом –
Я использую привязки PyLibTiff. И я решил это, посмотри на мой ответ! –