我正在使用mpirun和2个核心运行一个进程,当我在两个进程之间混合值时,它就会被杀死。两个进程使用大约15%的机器内存,即使混合时内存会增加,仍然应该有足够的内存。所以我假设用于在进程之间传递消息的内存量有限制。 如何找出此限制是什么以及如何将其删除?
我在mpirun死亡时得到的错误信息是:
File "Comm.pyx", line 864, in mpi4py.MPI.Comm.bcast (src/mpi4py.MPI.c:67787)
File "pickled.pxi", line 564, in mpi4py.MPI.PyMPI_bcast (src/mpi4py.MPI.c:31462)
File "pickled.pxi", line 93, in mpi4py.MPI._p_Pickle.alloc (src/mpi4py.MPI.c:26327)
SystemError: Negative size passed to PyBytes_FromStringAndSize
这是导致错误的代码:
sum_updates_j_k = numpy.zeros((self.col.J_total, self.K), dtype=numpy.float64))
comm.Reduce(self.updates_j_k, sum_updates_j_k, op=MPI.SUM)
sum_updates_j_k = comm.bcast(sum_updates_j_k, root=0)
代码通常有效,它只会遇到大量数据的问题,这会使我在进程之间交换的矩阵大小增加
答案 0 :(得分:2)
罪魁祸首可能是PyMPI_bcast()
代码中的以下行:
cdef int count = 0
...
if dosend: smsg = pickle.dump(obj, &buf, &count) # <----- (1)
with nogil: CHKERR( MPI_Bcast(&count, 1, MPI_INT, # <----- (2)
root, comm) )
cdef object rmsg = None
if dorecv and dosend: rmsg = smsg
elif dorecv: rmsg = pickle.alloc(&buf, count)
...
此处发生的事情是,首先使用(1)
在pickle.dump()
处序列化对象,然后在(2)
广播被检流的长度。
这里有两个问题,它们都与int
用于长度的事实有关。第一个问题是pickle.dump
内的整数强制转换,另一个问题是MPI_INT
用于传输pickle流的长度。这会将矩阵中的数据量限制为特定大小 - 即导致酸洗对象不大于2 GiB(2 31 -1字节)的大小。任何更大的对象都会导致整数溢出,从而导致count
中的负值。
这显然不是MPI问题,而是({或者?mpi4py
的一个特征中的错误。
答案 1 :(得分:1)
我最近遇到了与mpi4py相同的问题。正如Hristo Iliev在回答中指出的那样,这是一个腌菜问题。
这可以通过使用大写方法 comm.Reduce()
,comm.Bcast()
等,which do not resort to pickle来避免,而不是像小写方法那样comm.reduce()
。作为奖励,大写方法也应该更快一些。
实际上,您已经在使用comm.Reduce()
,所以我希望切换到comm.Bcast()
可以解决您的问题 - 它确实适用于我。
注意:大写方法的语法略有不同,但this tutorial可以帮助您入门。
例如,而不是:
sum_updates_j_k = comm.bcast(sum_updates_j_k, root=0)
你会用:
comm.Bcast(sum_updates_j_k, root=0)
答案 2 :(得分:0)
对于这种情况,拥有一个可以部分发送numpy
数组的函数是很有用的,例如:
from mpi4py import MPI
import math, numpy
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
def bcast_array_obj(obj = None, dtype = numpy.float64, root = 0):
"""Function for broadcasting of a numpy array object"""
reporter = 0 if root > 0 else 1
if rank == root:
for exp in range(11):
parts = pow(2, exp)
err = False
part_len = math.ceil(len(obj) / parts)
for part in range(parts):
part_begin = part * part_len
part_end = min((part + 1) * part_len, len(obj))
try:
comm.bcast(obj[part_begin: part_end], root = root)
except:
err = True
err *= comm.recv(source = reporter, tag = 2)
if err:
break
if err:
continue
comm.bcast(None, root = root)
print('The array was successfully sent in {} part{}'.\
format(parts, 's' if parts > 1 else ''))
return
sys.stderr.write('Failed to send the array even in 1024 parts')
sys.stderr.flush()
else:
obj = numpy.zeros(0, dtype = dtype)
while True:
err = False
try:
part_obj = comm.bcast(root = root)
except:
err = True
obj = numpy.zeros(0, dtype = dtype)
if rank == reporter:
comm.send(err, dest = root, tag = 2)
if err:
continue
if type(part_obj) != type(None):
frags = len(obj)
obj.resize(frags + len(part_obj))
obj[frags: ] = part_obj
else:
break
return obj
此功能自动确定打破输入数组的最佳部件数。
例如,
if rank != 0:
z = bcast_array_obj(root = 0)
else:
z = numpy.zeros(1000000000, dtype = numpy.float64)
bcast_array_obj(z, root = 0)
输出
The array was successfully sent in 4 parts
答案 3 :(得分:0)
显然这是 MPI 本身的问题,而不是 MPI4py。保存正在传输的数据大小的实际变量是一个有符号的 32 位整数,对于大约 2GB 的数据,它会溢出到负值。
Maximum amount of data that can be sent using MPI::Send
它之前也被作为 MPI4py 的问题提出here。