Я использовал этот script (см. Код в конце), чтобы оценить, является ли глобальный объект общим или скопированным, когда родительский процесс разветвляется.Многопроцессорность: почему массив numpy используется совместно с дочерними процессами, а список скопирован?
Вкратце, сценарий создает глобальный объект data
, а дочерний процесс обрабатывает итерацию по data
. Сценарий также отслеживает использование памяти для оценки того, был ли объект скопирован в дочерние процессы.
Вот результаты:
data = np.ones((N,N))
. Операция в дочернем процессе:data.sum()
. Результат:data
является общий (no copy)data = list(range(pow(10, 8)))
. Операция в дочернем процессе:sum(data)
. Результат:data
Номер копировано.data = list(range(pow(10, 8)))
. Операция в дочернем процессе:for x in data: pass
. Результат:data
Номер копировано.
Результат 1) ожидается из-за копирования на запись. Я немного озадачен результатами 2) и 3). Почему data
скопирован?
Script
import multiprocessing as mp
import numpy as np
import logging
import os
logger = mp.log_to_stderr(logging.WARNING)
def free_memory():
total = 0
with open('/proc/meminfo', 'r') as f:
for line in f:
line = line.strip()
if any(line.startswith(field) for field in ('MemFree', 'Buffers', 'Cached')):
field, amount, unit = line.split()
amount = int(amount)
if unit != 'kB':
raise ValueError(
'Unknown unit {u!r} in /proc/meminfo'.format(u = unit))
total += amount
return total
def worker(i):
x = data.sum() # Exercise access to data
logger.warn('Free memory: {m}'.format(m = free_memory()))
def main():
procs = [mp.Process(target = worker, args = (i,)) for i in range(4)]
for proc in procs:
proc.start()
for proc in procs:
proc.join()
logger.warn('Initial free: {m}'.format(m = free_memory()))
N = 15000
data = np.ones((N,N))
logger.warn('After allocating data: {m}'.format(m = free_memory()))
if __name__ == '__main__':
main()
Подробные результаты
Run 1 выход
[WARNING/MainProcess] Initial free: 25.1 GB [WARNING/MainProcess] After allocating data: 23.3 GB [WARNING/Process-2] Free memory: 23.3 GB [WARNING/Process-4] Free memory: 23.3 GB [WARNING/Process-1] Free memory: 23.3 GB [WARNING/Process-3] Free memory: 23.3 GB
Run 2 Выход
[WARNING/MainProcess] Initial free: 25.1 GB [WARNING/MainProcess] After allocating data: 21.9 GB [WARNING/Process-2] Free memory: 12.6 GB [WARNING/Process-4] Free memory: 12.7 GB [WARNING/Process-1] Free memory: 16.3 GB [WARNING/Process-3] Free memory: 17.1 GB
Run 3 Выход
[WARNING/MainProcess] Initial free: 25.1 GB [WARNING/MainProcess] After allocating data: 21.9 GB [WARNING/Process-2] Free memory: 12.6 GB [WARNING/Process-4] Free memory: 13.1 GB [WARNING/Process-1] Free memory: 14.6 GB [WARNING/Process-3] Free memory: 19.3 GB
Хорошо, поэтому я не могу повторить или сделать простой поиск без запуска копий. Разве это не делает копирование на запись близким к бесполезному? –
COW (copy-on-write) - это концепция ОС, которую Python наследует от 'fork()' на платформах, поддерживающих эту функцию. COW не был разработан с учетом многопроцессорности Python (mp), и mp Python не был разработан с учетом COW ;-) COW - это специфичная для платформы вещь, которая может быть или не быть полезной, в зависимости от приложения. Обратите внимание, что для многих типов объектов в CPython, refcount _not_ хранится «с основной массой данных объекта». В любом случае, COW первоначально была изобретена для реализации exec-after-fork, где преимущество заключается в том, что большая часть данных родительского процесса никогда не упоминается. –