多进程IOError:坏的消息长度

22

当我向map函数传递大参数时,出现了IOError: bad message length错误。我该如何避免?当N=1500或更大时会发生此错误。

代码如下:

import numpy as np
import multiprocessing

def func(args):
    i=args[0]
    images=args[1]
    print i
    return 0

N=1500       #N=1000 works fine

images=[]
for i in np.arange(N):
    images.append(np.random.random_integers(1,100,size=(500,500)))

iter_args=[]
for i in range(0,1):
    iter_args.append([i,images])

pool=multiprocessing.Pool()
print pool
pool.map(func,iter_args)
multiprocessing的文档中有一个名为recv_bytes的函数会引发IOError异常。这可能是因为它导致的吗?(详情请见https://python.readthedocs.org/en/v2.7.2/library/multiprocessing.html编辑 如果我使用images作为numpy数组而不是列表,我会得到另一个错误:SystemError: NULL result without error in PyObject_Call。以下是稍微不同的代码:
import numpy as np
import multiprocessing

def func(args):
    i=args[0]
    images=args[1]
    print i
    return 0

N=1500       #N=1000 works fine

images=[]
for i in np.arange(N):
    images.append(np.random.random_integers(1,100,size=(500,500)))
images=np.array(images)                                            #new

iter_args=[]
for i in range(0,1):
    iter_args.append([i,images])

pool=multiprocessing.Pool()
print pool
pool.map(func,iter_args)

编辑2 我使用的实际函数是:

def func(args):
    i=args[0]
    images=args[1]
    image=np.mean(images,axis=0)
    np.savetxt("image%d.txt"%(i),image)
    return 0

此外,iter_args中不包含相同的图像集:

iter_args=[]
for i in range(0,1):
    rand_ind=np.random.random_integers(0,N-1,N)
    iter_args.append([i,images[rand_ind]])

1
你正在传递3GB的图像。如果它们最初在文件系统上,也许你可以简单地将文件名排队。如果你正在生成它们,你可以使用多进程Array类来创建共享内存中的数据,这样实际上在队列中的数据量(例如关于共享内存的信息)就会更小。 - Patrick Maupin
1
在Ubuntu上我也遇到了同样的错误 IOError: bad message length - Padraic Cunningham
2
func()需要同时处理所有1500张图片,还是可以一次只处理一张图片? - Joseph Sheedy
1
你想要实现什么?你提供的代码似乎没有理由使用 multiprocessing:它将启动一个单一子进程并将所有图片传递给该进程。难道你不是真正想要许多子进程,每个进程一次处理一张图片吗? - Daniel Renshaw
1
我在我的答案中添加了一个解决方案(不使用多进程)。在合理现代硬件上,它可以快速处理1500张图像,而无需使用多进程。 - Joseph Sheedy
显示剩余4条评论
5个回答

12

您正在创建一个池并将所有图像一次性发送到func()函数。如果您可以一次只处理单个图像,尝试使用类似于以下内容的代码,这将在Python 2.7.10上仅需35秒即可完成N=10000:

import numpy as np
import multiprocessing

def func(args):
    i = args[0]
    img = args[1]
    print "{}: {} {}".format(i, img.shape, img.sum())
    return 0

N=10000

images = ((i, np.random.random_integers(1,100,size=(500,500))) for i in xrange(N))
pool=multiprocessing.Pool(4)
pool.imap(func, images)
pool.close()
pool.join()
关键在于使用迭代器,这样就不必一次性将所有数据都保存在内存中。例如,我把保存有所有数据的数组转换成了生成器表达式,仅在需要时创建图像。您可以修改此方法以从磁盘或其他位置加载图像。我还使用了pool.imap而不是pool.map。
如果可以的话,请尝试在工作函数中加载图像数据。现在,您必须序列化所有数据并将其发送到另一个进程中。如果您的图像数据更大,这可能会成为瓶颈点。
[现在我们知道func必须同时处理所有图像的更新]
您可以对图像进行迭代平均值计算。以下解决方案不使用多进程。要使用多进程,您可以将图像分成几个部分,并将这些部分分配给池。
import numpy as np

N=10000
shape = (500,500)

def func(images):
    average = np.full(shape, 0)
    for i, img in images:
        average += img / N
    return average

images = ((i, np.full(shape,i)) for i in range(N))

print func(images)

这里的关键点是你需要关闭并加入池中(使用 pool.close()pool.join())。 - BoltzmannBrain

1
Python很可能会将您的数据加载到RAM内存中,因此您需要确保该内存可用。您已经检查过计算机内存使用情况了吗?
另外,正如Patrick所提到的,您正在加载3GB的数据,请确保使用64位版本的Python,因为您正在达到32位内存限制。这可能会导致进程崩溃:32 vs 64 bits Python 另一个改进是使用Python 3.4而不是2.7。Python 3的实现似乎针对非常大的范围进行了优化,请参见Python3 vs Python2 list/generator range performance

1
当运行您的程序时,它实际上会给我一个清晰的错误提示:
OSError: [Errno 12] Cannot allocate memory

就像其他用户提到的那样,解决您的问题的方法很简单:增加内存(很多)或更改程序处理图像的方式。

使用如此多的内存的原因是因为您在模块级别上分配了图像的内存。因此,当多进程fork您的进程时,它也会复制所有图像(根据python multiprocessing中的共享内存对象,这不是免费的),这是不必要的,因为您还将图像作为参数传递给函数,多进程模块还使用ipc和pickle进行复制,这仍然可能导致缺乏内存。尝试其他用户提出的建议之一。


1
这就是解决问题的方法:声明图片为全局变量。
import numpy as np
import multiprocessing


N=1500       #N=1000 works fine

images=[]
for i in np.arange(N):
    images.append(np.random.random_integers(1,100,size=(500,500)))

def func(args):
    i=args[0]
    images=images
    print i
    return 0

iter_args=[]
for i in range(0,1):
    iter_args.append([i])

pool=multiprocessing.Pool()
print pool
pool.map(func,iter_args)

1
据我所知,这种方法会将所有图像复制到每个进程中,使其缩放非常差。 - Jules G.M.

0
当传递大对象时,出现“IOError: bad message length”的原因是由于旧版CPython版本(3.2及更早版本)中硬编码的限制为0x7fffffff字节或约2.1GB:https://github.com/python/cpython/blob/v2.7.5/Modules/_multiprocessing/multiprocessing.h#L182

这个CPython更改集(在CPython 3.3及更高版本中)删除了硬编码限制:https://github.com/python/cpython/commit/87cf220972c9cb400ddcd577962883dcc5dca51a#diff-4711c9abeca41b149f648d4b3c15b6a7d2baa06aa066f46359e4498eb8e39f60L182


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接