22

I get an IOError: bad message length when passing large arguments to the map function. How can I avoid this? The error occurs when I set N=1500 or bigger.

The code is:

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)

In the docs of multiprocessing there is the function recv_bytes that raises an IOError. Could it be because of this? (https://python.readthedocs.org/en/v2.7.2/library/multiprocessing.html)

EDIT If I use images as a numpy array instead of a list, I get a different error: SystemError: NULL result without error in PyObject_Call. A bit different code:

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)

EDIT2 The actual function that I use is:

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

Additionally, the iter_args do not contain the same set of images:

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]])
Andy
  • 1,072
  • 2
  • 19
  • 33
  • 1
    You're passing 3GB of images. If they start out on the file system, perhaps you could simply queue the file names. If you are generating them, you could use the multiprocessing Array class to create data in shared memory, so the amount of data actually on the queue (e.g. the information about the shared memory) is smaller. – Patrick Maupin Jul 30 '15 at 22:55
  • If you want to *avoid* the problem, you can make `func` *load* the corresponding image, rather than passsing it as an argument. This is definitely a bug that [must be reported](https://docs.python.org/3/bugs.html), but the fix won't get to you soon and I don't recommend using nightly builds if you "just want to code" – Felipe Lema Jul 31 '15 at 13:35
  • I cannot replicate the issue using python 2.7.10 – rll Jul 31 '15 at 15:03
  • @rll did you check both code snippets? how long does it take to run the program? – Andy Aug 02 '15 at 10:27
  • 1
    Same error for me on ubuntu `IOError: bad message length` – Padraic Cunningham Aug 02 '15 at 21:48
  • 2
    Does func() need to operate on all 1500 images at the same time, or can it work on a single image at once? – Joseph Sheedy Aug 03 '15 at 18:07
  • 1
    What are you trying to achieve? The code you give seems to be using multiprocessing without reason: it will spin up a *single* child process and pass *all* of the images to that one process. Don't you actually want many child processes, each processing a single image at a time? – Daniel Renshaw Aug 05 '15 at 14:18
  • @velotron@Daniel Renshaw: I updated my question by EDIT2. Due to the minimal example issue, my goal was not clear. With the actual function `func` I calculate the mean of all the images. – Andy Aug 05 '15 at 20:51
  • 1
    I added a solution (without multiprocessing) to my answer. It will handle 1500 images quickly on reasonably modern hardware without multiprocessing. – Joseph Sheedy Aug 05 '15 at 21:39

5 Answers5

12

You're creating a pool and sending all the images at once to func(). If you can get away with working on a single image at once, try something like this, which runs to completion with N=10000 in 35s with Python 2.7.10 for me:

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()

The key here is to use iterators so you don't have to hold all the data in memory at once. For instance I converted images from an array holding all the data to a generator expression to create the image only when needed. You could modify this to load your images from disk or whatever. I also used pool.imap instead of pool.map.

If you can, try to load the image data in the worker function. Right now you have to serialize all the data and ship it across to another process. If your image data is larger, this might be a bottleneck.

[update now that we know func has to handle all images at once]

You could do an iterative mean on your images. Here's a solution without using multiprocessing. To use multiprocessing, you could divide your images into chunks, and farm those chunks out to the pool.

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)
Joseph Sheedy
  • 6,296
  • 4
  • 30
  • 31
1

Python is likely to load your data in your RAM memory and you need this memory to be available. Have you checked your computer memory usage ?

Also as Patrick mentioned, you're loading 3GB of data, make sure you use the 64 bits version of Python as you are reaching the 32 bits memory contraint. This could cause your process to crash : 32 vs 64 bits Python

Another improvement would be to use python 3.4 instead of 2.7. Python 3 implementation seems to be optimized for very large ranges, see Python3 vs Python2 list/generator range performance

Community
  • 1
  • 1
1

When running your program it actually gives me an clear error:

OSError: [Errno 12] Cannot allocate memory

Like mentioned by other users, the solution to your problem is simple add memory(a lot) or change the way your program is handling the images.

The reason it's using so much memory is because you allocate your memory for your images on a module level. So when multiprocess forks your process it's also copying all the images (which isn't free according to Shared-memory objects in python multiprocessing), this is not necessary because you are also giving the images as an argument to the function which the multiprocess module also copies using ipc and pickle, this would still likely result in a lack of memory. Try one of the proposed solutions given by the other users.

Community
  • 1
  • 1
joebie13
  • 21
  • 1
1

This is what solved the problem: declaring the images global.

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)
Andy
  • 1,072
  • 2
  • 19
  • 33
  • 1
    this copies all of the images to each of the processes afaik, making this method really really poor at scaling – Jules G.M. May 17 '17 at 14:47
0

The reason why you get IOError: bad message length when passing around large objects is due to a hard coded limit in older CPython versions (3.2 and earlier) of 0x7fffffff Bytes or around 2.1GB: https://github.com/python/cpython/blob/v2.7.5/Modules/_multiprocessing/multiprocessing.h#L182

This CPython changeset (which is in CPython 3.3 and later) removed the hard coded limit: https://github.com/python/cpython/commit/87cf220972c9cb400ddcd577962883dcc5dca51a#diff-4711c9abeca41b149f648d4b3c15b6a7d2baa06aa066f46359e4498eb8e39f60L182

Elias
  • 1,367
  • 11
  • 25