如何删除openmpi进程的内存限制?

时间:2013-11-16 20:53:09

标签: python pickle openmpi mpi4py

我正在使用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) 

代码通常有效,它只会遇到大量数据的问题,这会使我在进程之间交换的矩阵大小增加

4 个答案:

答案 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