Совместное Использование Большого Массива Numpy Только Для Чтения Между Многопроцессорными Процессами
у меня есть 60 ГБ SciPy массив (Матрица) я должен разделить между 5+ multiprocessing
Process
объекты. Я видел numpy-sharedmem и читал эта дискуссия в списке SciPy. Есть два подхода--numpy-sharedmem
и с помощью multiprocessing.RawArray()
и отображение NumPy dtype
ь ctype
ы. Теперь, numpy-sharedmem
Кажется, это путь, но я еще не видел хороший пример ссылки. Мне не нужны никакие блокировки, так как массив (фактически матрица) будет доступен только для чтения. Теперь, из-за его размера, я хотел бы чтобы избежать копирования. Это звучит как правильный метод заключается в создании только копия массива в виде sharedmem
массив, а затем передать его в Process
объекты? Несколько конкретных вопросов:
какой самый лучший способ, на самом деле проходят sharedmem ручки для Sub-
Process()
Эс? Нужна ли мне очередь только для передачи одного массива? Может быть, лучше трубка? Могу ли я просто передать его в качестве аргументаProcess()
подкласс init (где я предполагаю это маринованный)?в обсуждении, которое я связал выше, есть упоминание о
numpy-sharedmem
не будучи 64bit-безопасным? Я определенно использую некоторые структуры, которые не являются 32-разрядными адресными.есть ли компромисс в
RawArray()
подход? Медленнее, педераст?нужен ли мне ctype Для к dtype отображение для библиотеки numpy-sharedmem способ?
есть ли у кого-нибудь пример некоторого кода с открытым исходным кодом делать это? Я очень практический ученый, и трудно заставить это работать без какого-либо хорошего примера, чтобы посмотреть.
если есть какая-либо дополнительная информация, которую я могу предоставить, чтобы помочь прояснить это для других, прокомментируйте, и я добавлю. Спасибо!
это должно работать на Ubuntu Linux и может быть Mac OS, но переносимость не является огромной проблемой.
5 ответов:
@Velimir Mlaker дал отличный ответ. Я подумал, что могу добавить несколько комментариев и крошечный пример.
(Я не смог найти много документации по sharedmem-это результаты моих собственных экспериментов.)
- нужно ли передавать дескрипторы при запуске подпроцесса или после его запуска? Если это только первый, вы можете просто использовать
target
иargs
аргументыProcess
. Это потенциально лучше, чем использование глобального переменная.- на странице обсуждения, которую вы связали, похоже, что поддержка 64-разрядного Linux была добавлена в sharedmem некоторое время назад, поэтому это может быть не проблема.
- Я не знаю об этом.
- нет. См. пример ниже.
пример
#!/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
topickle
данные для ребенка, которые были бы крайне неэффективны или невозможны в вашем случае. На 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 один требуются засолить данные к ребенка, если нужны данные, которые были созданы во время выполнения родителя.
вас может заинтересовать крошечный кусочек кода, который я написал:github.com/vmlaker/benchmark-sharedmem
единственный файл, проценты
main.py
. Это эталон numpy-sharedmem -- код просто передает массивы (либоnumpy
илиsharedmem
) для порожденных процессов, через трубу. Рабочие просто звонятsum()
на данных. Меня интересует только сравнение времени передачи данных между двумя реализациями.я тоже писал другой, более сложный код: github.com/vmlaker/sherlock.
здесь я использую numpy-sharedmem модуль для обработки изображений в реальном времени с помощью OpenCV -- изображения представляют собой массивы NumPy, согласно более новой версии OpenCV
cv2
API. Изображения, фактически ссылки на них, разделяются между процессами через объект словаря, созданный изmultiprocessing.Manager
(в отличие от использования очереди или трубы.) Я получаю большие улучшения производительности по сравнению с использование простых массивов NumPy.труба против очереди:
по моему опыту, IPC с трубой быстрее, чем очередь. И это имеет смысл, так как очередь добавляет блокировку, чтобы сделать ее безопасной для нескольких производителей/потребителей. Но если у вас есть только два процесса, говорящие взад и вперед, безопасно использовать Pipe, или, как говорится в документах:
... нет никакого риска повреждения от процессов, использующих разные концы трубы в то же время время.
sharedmem
безопасность:главная проблема
sharedmem
модуль-это возможность утечки памяти при некрасивом выходе из программы. Это описано в длительной дискуссии здесь. Хотя 10 апреля 2011 года Sturla упоминает исправление утечки памяти, с тех пор я все еще испытывал утечки, используя оба репозитория, собственный Sturla Molden на GitHub (github.com/sturlamolden/sharedmem-numpy) и Крис Ли-Мессер включен Bitbucket (bitbucket.org/cleemesser/numpy-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))
обзоры ответы:
вы также можете найти полезным взглянуть на документацию для pyro как если бы вы могли разделить свою задачу соответствующим образом, вы могли бы использовать ее для выполнения различных разделов на разных машинах, а также на разных ядрах в одной машине.