0

I'm trying to speed up a section of my code using parallel processing in python, but I'm having trouble getting it to work right, or even find examples that are relevant to me.

The code produces a low-polygon version of an image using Delaunay triangulation, and the part that's slowing me down is finding the mean values of each triangle.

I've been able to get a good speed increase by vectorizing my code, but hope to get more using parallelization:

The code I'm having trouble with is an extremely simple for loop:

for tri in tris:
        lopo[tridex==tri,:] = np.mean(hipo[tridex==tri,:],axis=0)

The variables referenced are as follows.

tris - a unique python list of all the indices of the triangles

lopo - a Numpy array of the final low-polygon version of the image

hipo - a Numpy array of the original image

tridex - a Numpy array the same size as the image. Each element represents a pixel and stores the triangle that the pixel lies within

I can't seem to find a good example that uses multiple numpy arrays as input, with one of them shared.

I've tried multiprocessing (with the above snippet wrapped in a function called colorImage):

p = Process(target=colorImage, args=(hipo,lopo,tridex,ppTris))
p.start()
p.join()

But I get a a broken pipe error immediately.

Bill
  • 698
  • 1
  • 5
  • 22

1 Answers1

1

So the way that Python's multiprocessing works (for the most part) is that you have to designate the individual threads that you want to run. I made a brief introductory tutorial here: http://will-farmer.com/parallel-python.html

In your case, what I would recommend is split tris into a bunch of different parts, each equally sized, each that represents a "worker". You can split this list with numpy.split() (documentation here: http://docs.scipy.org/doc/numpy/reference/generated/numpy.split.html).

Then for each list in tri, we use the Threading and Queue modules to designate 8 workers.

import numpy as np
# split into 8 different lists
tri_lists = np.split(tris, 8)
# Queues are threadsafe
return_values = queue.Queue()
threads = []
def color_image(q, tris, hipo, tridex):
    """ This is the function we're parallelizing """
    for tri in tris:
        return_values.put(np.mean(hipo[tridex==tri,:], axis=0))
# Now we run the jobs
for i in range(8):
    threads.append(threading.Thread(
        target=color_image,
        args=(return_values, tri_lists[i], hipo, tridex)))
# Now we have to cleanup our results
# First get items from queue
results = [item for item in return_values.queue]
# Now set values in lopo
for i in range(len(results)):
    for t in tri_lists[i]:
        lopo[tridex==t, :] = results[i]

This isn't the cleanest way to do it, and I'm not sure if it works since I can't test it, but this is a decent way to do it. The parallelized part is now np.mean(), while setting the values is not parallelized.

If you want to also parallelize the setting of the values, you'll have to have a shared variable, either using the Queue, or with a global variable.

See this post for a shared global variable: Python Global Variable with thread

Community
  • 1
  • 1
Will
  • 996
  • 1
  • 6
  • 8
  • Does threading not allow for setting the new values? I haven't fully checked, but I do believe that the threads won't try to write to the same pixels. Doing the mean calculations and the writing at the same time should give me the large speed-up I'm hoping to see. – Bill Apr 13 '15 at 02:52
  • That will work as well, but I've never worked with a shared variable over threads. As long as you're just setting the pixels, you should be fine. And yes, that would also give you a larger speedup. See that SO link I provided for an example. – Will Apr 13 '15 at 06:02
  • I seem to be having trouble running even simple functions in parallel. Do you know of any reason why a script (run in IDLE) would run in series even when using parallelization modules? Might there be some setting on my computer that limits how many threads IDLE can use? – Bill Apr 14 '15 at 16:16
  • I have no idea, maybe try running my commands from my webpage? See if those work? I don't use IDLE, but I wouldn't imagine there would be a thread limit. Try doing some basic tests first and make sure that there is no limit. If you're still having issues, post your code (maybe even in edit of original post) and I'll see if I can help. – Will Apr 14 '15 at 18:26