Общая память и мультипроцессорная обработка



Из этого вопроса и его ответов я думаю, что я понимаю, почему этот код python:



big_list = [
{j: 0 for j in range(200000)}
for i in range(60)
]

def worker():
for dic in big_list:
for key in dic:
pass
print "."
time.sleep(0.2)

w = multiprocessing.Process(target=worker)
w.start()

time.sleep(3600)


Продолжает использовать все больше и больше памяти во время выполнения: это потому, что дочерний процесс обновляет счетчик ссылок на объект общей памяти в цикле, вызывая "копирование на запись" mecanism (я могу наблюдать, как уменьшается свободная память через cat /proc/meminfo | grep MemFree).



Однако чего я не понимаю, так это почему происходит то же самое, если итерация происходит в Родительском коде. чем в ребенке:

def worker():
time.sleep(3600)

w = multiprocessing.Process(target=worker)
w.start()

for dic in big_list:
for key in dic:
pass
print "."
time.sleep(0.2)


Ребенку даже не нужно знать о существовании big_list.

В этом небольшом примере я могу решить проблему, поместив del big_list в дочернюю функцию, но иногда ссылки на переменные недоступны, как это, поэтому все усложняется.



Почему происходит этот меканизм и как я могу избежать его должным образом?

548   1  

1 ответ:

После fork() оба родителя и потомок "видят" одно и то же адресное пространство. В первый раз, когда либо изменяет память по общему адресу, механизм копирования при записи (COW) должен клонировать страницу, содержащую этот адрес. Таким образом, для целей создания страниц COW не имеет значения, происходят ли мутации у ребенка или у родителя.

Во втором фрагменте кода Вы упустили самую важную часть: где именно был создан big_list. С тех пор, как ты сказал, что тебе все сойдет с рук. del big_list в ребенке big_list, вероятно, существовал до того, как вы разветвили рабочий процесс. Если это так, то - как указано выше - для вашего симптома не имеет значения, изменяется ли big_list у родителя или у ребенка.

Чтобы избежать этого, создайте big_list после создания вашего дочернего процесса. Тогда адресное пространство, в котором он живет, не будет общим. Или, в Python 3.4 или более поздней версии, используйте multiprocessing.set_start_method('spawn'). Тогда fork() не будет использоваться для создания дочерних процессов, а адресное пространство вообще не будет общим (что всегда имеет место на Windows, которая не имеет fork()).

Comments

    Ничего не найдено.