У меня есть массив SciPy (матрица) объемом 60 ГБ, который я должен использовать для более чем 5 multiprocessing
Process
объектов. Я видел numpy-sharedmem и читал это обсуждение в списке SciPy. Кажется, есть два подхода - numpy-sharedmem
и использование a, multiprocessing.RawArray()
и сопоставление NumPy dtype
s с ctype
s. numpy-sharedmem
Кажется , это верный путь, но я еще не видел хорошего справочного примера. Мне не нужны никакие блокировки, так как массив (фактически матрица) будет доступен только для чтения. Теперь из-за его размера я бы не хотел копировать. Это звучит , как правильный метод , чтобы создать только копию массива в виде sharedmem
массива, а затем передать его на Process
объекты? Пара конкретных вопросов:
Как лучше всего передать дескрипторы sharedmem подчиненным
Process()
? Нужна ли мне очередь только для передачи одного массива? Лучше бы трубка? Могу ли я просто передать его как аргументProcess()
init подкласса (где я предполагаю, что он маринованный)?В обсуждении, которое я связал выше, есть упоминание о
numpy-sharedmem
том, что он небезопасен для 64 бит? Я определенно использую некоторые структуры, которые не имеют 32-битной адресации.Есть ли компромисс в
RawArray()
подходе? Медленнее, медленнее?Мне нужно какое-либо сопоставление ctype-to-dtype для метода numpy-sharedmem?
Есть ли у кого-нибудь пример такого кода OpenSource? Я очень практичный человек, и мне трудно заставить это работать без какого-либо хорошего примера.
Если есть дополнительная информация, которую я могу предоставить, чтобы прояснить это для других, прокомментируйте, и я добавлю. Благодарность!
Это должно работать в Ubuntu Linux и, возможно, Mac OS, но переносимость не является большой проблемой.
multiprocessing
сделать копию всего этого для каждого процесса.target_function
). Операционная система будет копировать части родительской памяти в дочернюю память только после модификации.Ответы:
@Velimir Mlaker дал отличный ответ. Я подумал, что могу добавить несколько комментариев и небольшой пример.
(Я не смог найти много документации по sharedmem - это результаты моих собственных экспериментов.)
target
иargs
аргументыProcess
. Это потенциально лучше, чем использование глобальной переменной.пример
#!/usr/bin/env python from multiprocessing import Process import sharedmem import numpy def do_work(data, start): data[start] = 0; def split_work(num): n = 20 width = n/num shared = sharedmem.empty(n) shared[:] = numpy.random.rand(1, n)[0] print "values are %s" % shared processes = [Process(target=do_work, args=(shared, i*width)) for i in xrange(num)] for p in processes: p.start() for p in processes: p.join() print "values are %s" % shared print "type is %s" % type(shared[0]) if __name__ == '__main__': split_work(4)
Выход
values are [ 0.81397784 0.59667692 0.10761908 0.6736734 0.46349645 0.98340718 0.44056863 0.10701816 0.67167752 0.29158274 0.22242552 0.14273156 0.34912309 0.43812636 0.58484507 0.81697513 0.57758441 0.4284959 0.7292129 0.06063283] values are [ 0. 0.59667692 0.10761908 0.6736734 0.46349645 0. 0.44056863 0.10701816 0.67167752 0.29158274 0. 0.14273156 0.34912309 0.43812636 0.58484507 0. 0.57758441 0.4284959 0.7292129 0.06063283] type is <type 'numpy.float64'>
Этот связанный вопрос может быть полезен.
источник
Если вы работаете в Linux (или любой другой POSIX-совместимой системе), вы можете определить этот массив как глобальную переменную.
multiprocessing
используетсяfork()
в Linux, когда запускает новый дочерний процесс. Вновь созданный дочерний процесс автоматически разделяет память со своим родителем, пока он не изменяет ее ( копирование при записи механизм ).Поскольку вы говорите: «Мне не нужны никакие блокировки, поскольку массив (на самом деле матрица) будет доступен только для чтения», использование этого поведения будет очень простым и все же чрезвычайно эффективным подходом: все дочерние процессы будут иметь доступ те же данные в физической памяти при чтении этого большого массива numpy.
Не передать свой массив в
Process()
конструктор, это будет инструктироватьmultiprocessing
кpickle
данным для ребенка, что было бы крайне неэффективно или невозможно в вашем случае. В Linux сразу послеfork()
дочернего элемента находится точная копия родительского элемента, использующая ту же физическую память, поэтому все, что вам нужно сделать, - это убедиться, что переменная Python, 'содержащая' матрицу, доступна изtarget
функции, которую вы передаетеProcess()
. Обычно этого можно добиться с помощью «глобальной» переменной.Пример кода:
from multiprocessing import Process from numpy import random global_array = random.random(10**4) def child(): print sum(global_array) def main(): processes = [Process(target=child) for _ in xrange(10)] for p in processes: p.start() for p in processes: p.join() if __name__ == "__main__": main()
В Windows - которая не поддерживает
fork()
-multiprocessing
использует вызов win32 APICreateProcess
. Он создает совершенно новый процесс из любого исполняемого файла. Вот почему в Windows требуется передавать данные дочернему элементу, если нужны данные, которые были созданы во время выполнения родительского элемента.источник
multiprocessing
иначе дочерние процессы могут зависнуть ( обычно используется 1 / n одного процессора, а не n процессоров) при выполнении операций линейной алгебры над общим глобальным массивом / матрицей. Известный многопоточной конфликт с OpenBLAS , кажется, распространяется на Pythonmultiprocessing
fork
для передачи заданных параметровProcess
вместо их сериализации? То есть, нельзяfork
ли применить к родительскому процессу непосредственно передchild
вызовом, чтобы значение параметра все еще было доступно из ОС? Казалось бы, более эффективно, чем сериализация?fork()
это недоступно в Windows, об этом было сказано в моем ответе и несколько раз в комментариях. Я знаю , что это был ваш первоначальный вопрос, и я ответил ему четыре комментария выше этого : «компромисс , чтобы использовать тот же метод передачи параметров на обеих платформах по умолчанию, для лучшей ремонтопригодности и для обеспечения равного поведения.». Оба способа имеют свои преимущества и недостатки, поэтому в Python 3 пользователю предоставляется большая гибкость в выборе метода. Это обсуждение не является продуктивным без обсуждения деталей, чего мы не должны здесь делать.Возможно, вас заинтересует небольшой фрагмент кода, который я написал: github.com/vmlaker/benchmark-sharedmem
Единственный интересующий файл - это
main.py
. Это эталон numpy-sharedmem - код просто передает массивы (илиnumpy
илиsharedmem
) порожденным процессам через Pipe. Рабочие просто обращаютсяsum()
к данным. Меня интересовало только сравнение времени передачи данных между двумя реализациями.Я также написал другой, более сложный код: github.com/vmlaker/sherlock .
Здесь я использую модуль numpy-sharedmem для обработки изображений в реальном времени с помощью OpenCV - изображения представляют собой массивы NumPy в соответствии с новым
cv2
API OpenCV . Изображения, фактически ссылки на них, распределяются между процессами через объект словаря, созданный изmultiprocessing.Manager
(в отличие от использования Queue или Pipe.) Я получаю большие улучшения производительности по сравнению с использованием простых массивов NumPy.Труба против очереди :
По моему опыту, IPC с Pipe быстрее, чем Queue. И это имеет смысл, поскольку Queue добавляет блокировку, чтобы сделать ее безопасной для нескольких производителей / потребителей. Труба - нет. Но если у вас есть только два процесса, которые разговаривают друг с другом, безопасно использовать Pipe или, как говорится в документации:
sharedmem
безопасность :Основная проблема с
sharedmem
модулем - это возможность утечки памяти при некорректном выходе из программы. Это описано в длительной дискуссии здесь . Хотя 10 апреля 2011 г. Стурла упоминает исправление утечки памяти, с тех пор я все еще сталкивался с утечками, используя оба репозитория, собственный репозиторий Стурлы Молдена на GitHub ( github.com/sturlamolden/sharedmem-numpy ) и Криса Ли-Мессера на Bitbucket ( bitbucket.org/cleemesser/numpy-sharedmem ).источник
sharedmem
кажется большой проблемой. Есть какие-нибудь подсказки для решения этого?sharedmem
, для справки , хранителей двух репозиториев модуля с открытым исходным кодом .Если ваш массив такой большой, вы можете использовать
numpy.memmap
. Например, если у вас есть массив, хранящийся на диске, скажем'test.array'
, вы можете использовать одновременные процессы для доступа к данным в нем даже в режиме «записи», но ваш случай проще, поскольку вам нужен только режим «чтения».Создание массива:
a = np.memmap('test.array', dtype='float32', mode='w+', shape=(100000,1000))
Затем вы можете заполнить этот массив так же, как и обычный массив. Например:
a[:10,:100]=1. a[10:,100:]=2.
Данные сохраняются на диске, когда вы удаляете переменную
a
.Позже вы можете использовать несколько процессов, которые будут обращаться к данным в
test.array
:# read-only mode b = np.memmap('test.array', dtype='float32', mode='r', shape=(100000,1000)) # read and writing mode c = np.memmap('test.array', dtype='float32', mode='r+', shape=(100000,1000))
Связанные ответы:
Работа с большими данными в python и numpy, не хватает оперативной памяти, как сохранить частичные результаты на диске?
Можно ли сопоставить разрозненные данные на диске с массивом с помощью Python?
источник
Вам также может быть полезно взглянуть на документацию по pyro, как если бы вы могли правильно разделить свою задачу, вы могли бы использовать ее для выполнения разных разделов на разных машинах, а также на разных ядрах на одной машине.
источник
Почему бы не использовать многопоточность? Ресурсы основного процесса могут совместно использоваться его потоками изначально, поэтому многопоточность, очевидно, является лучшим способом совместного использования объектов, принадлежащих основному процессу.
Если вас беспокоит механизм GIL в Python, возможно, вы можете прибегнуть к использованию
nogil
ofnumba
.источник