Создание общего массива NumPy для всех процессов
Я прочитал довольно много вопросов о so об обмене массивами, и это кажется достаточно простым для простых массивов, но я застрял, пытаясь заставить его работать для массива, который у меня есть.
import numpy as np
data=np.zeros(250,dtype='float32, (250000,2)float32')
Я попытался преобразовать это в общий массив, пытаясь как-то сделать mp.Array
принять data
, Я также попытался создать массив, используя ctypes как таковые:
import multiprocessing as mp
data=mp.Array('c_float, (250000)c_float',250)
единственный способ, которым мне удалось заставить мой код работать, - это не передача данных в функцию, а передавая закодированную строку для несжатого/декодированного, это, однако, заканчивается вызовом N (количество строк) процессов, которые кажутся избыточными. Моя желаемая реализация основана на разрезании списка двоичных строк на x (количество процессов) и передаче этого куска, data
и index
процессам, которые работают кроме этого data
изменяется локально, следовательно, вопрос о как сделать общий любой пример работы с пользовательским (вложенные) массив numpy уже был бы большой помощью.
PS: этот вопрос является продолжением Python мульти-обработка
2 ответов
обратите внимание, что вы можете начать с массива сложных dtype:
In [4]: data = np.zeros(250,dtype='float32, (250000,2)float32')
и просмотрите его как массив однородного dtype:
In [5]: data2 = data.view('float32')
и позже преобразуйте его обратно в сложный dtype:
In [7]: data3 = data2.view('float32, (250000,2)float32')
изменение dtype-очень быстрая операция; это не влияет на базовые данные, только на то, как NumPy интерпретирует его. Таким образом, изменение dtype практически без затрат.
Итак, что вы читали о массивах с простыми (однородными) dtypes может быть легко применяется к вашему сложному dtype с трюком выше.
код ниже заимствует много идей из ответ Дж. Ф. Себастьяна, здесь.
import numpy as np
import multiprocessing as mp
import contextlib
import ctypes
import struct
import base64
def decode(arg):
chunk, counter = arg
print len(chunk), counter
for x in chunk:
peak_counter = 0
data_buff = base64.b64decode(x)
buff_size = len(data_buff) / 4
unpack_format = ">%dL" % buff_size
index = 0
for y in struct.unpack(unpack_format, data_buff):
buff1 = struct.pack("I", y)
buff2 = struct.unpack("f", buff1)[0]
with shared_arr.get_lock():
data = tonumpyarray(shared_arr).view(
[('f0', '<f4'), ('f1', '<f4', (250000, 2))])
if (index % 2 == 0):
data[counter][1][peak_counter][0] = float(buff2)
else:
data[counter][1][peak_counter][1] = float(buff2)
peak_counter += 1
index += 1
counter += 1
def pool_init(shared_arr_):
global shared_arr
shared_arr = shared_arr_ # must be inherited, not passed as an argument
def tonumpyarray(mp_arr):
return np.frombuffer(mp_arr.get_obj())
def numpy_array(shared_arr, peaks):
"""Fills the NumPy array 'data' with m/z-intensity values acquired
from b64 decoding and unpacking the binary string read from the
mzXML file, which is stored in the list 'peaks'.
The m/z values are assumed to be ordered without validating this
assumption.
Note: This function uses multi-processing
"""
processors = mp.cpu_count()
with contextlib.closing(mp.Pool(processes=processors,
initializer=pool_init,
initargs=(shared_arr, ))) as pool:
chunk_size = int(len(peaks) / processors)
map_parameters = []
for i in range(processors):
counter = i * chunk_size
# WARNING: I removed -1 from (i + 1)*chunk_size, since the right
# index is non-inclusive.
chunk = peaks[i*chunk_size : (i + 1)*chunk_size]
map_parameters.append((chunk, counter))
pool.map(decode, map_parameters)
if __name__ == '__main__':
shared_arr = mp.Array(ctypes.c_float, (250000 * 2 * 250) + 250)
peaks = ...
numpy_array(shared_arr, peaks)
если вы можете гарантировать, что различные процессы, которые выполняют задания
if (index % 2 == 0):
data[counter][1][peak_counter][0] = float(buff2)
else:
data[counter][1][peak_counter][1] = float(buff2)
никогда не конкурировать, чтобы изменить данные в тех же местах, то я считаю, что вы можете отказаться от использования блокировки
with shared_arr.get_lock():
но я не обращал внимания ваш код достаточно хорошо, чтобы знать наверняка, на всякий случай я включил замок.
from multiprocessing import Process, Array
import numpy as np
import time
import ctypes
def fun(a):
a[0] = -a[0]
while 1:
time.sleep(2)
#print bytearray(a.get_obj())
c=np.frombuffer(a.get_obj(),dtype=np.float32)
c.shape=3,3
print 'haha',c
def main():
a = np.random.rand(3,3).astype(np.float32)
a.shape=1*a.size
#a=np.array([[1,3,4],[4,5,6]])
#b=bytearray(a)
h=Array(ctypes.c_float,a)
print "Originally,",h
# Create, start, and finish the child process
p = Process(target=fun, args=(h,))
p.start()
#p.join()
a.shape=3,3
# Print out the changed values
print 'first',a
time.sleep(3)
#h[0]=h[0]+1
print 'main',np.frombuffer(h.get_obj(), dtype=np.float32)
if __name__=="__main__":
main()