Michael Fan Zhang
Michael Fan Zhang

Reputation: 261

why get() in multiprocessing takes so much time in Python?

I am trying to use multi-processing in Python for face recognition on a Raspberry Pi. To make full use of all the 4 cores, I used the multi-thread concept. Below is part of my (pseudo-)code:

count = 1

while True:

    image = cap.read

    if count == 1:
        r1 = pool.apply_async(func, [image]) # this is the image process module
        output = r2.get()  # this is used to get the results from processor #2
        showimage(output)  # show the processed results
    elif count == 2:
        r2 = pool.apply_async(func, [image]) # this is the image process module
        output = r3.get()  # this is used to get the results from processor #3
        showimage(output)  # show the processed results
    elif count == 3:
        r3 = pool.apply_async(func, [image]) # this is the image process module
        output = r4.get()  # this is used to get the results from processor #4
        showimage(output)  # show the processed results
    elif count == 4:
        r4 = pool.apply_async(func, [image]) # this is the image process module
        output = r1.get()  # this is used to get the results from processor #1
        showimage(output)  # show the processed results
        count = 0
        count += 1

I understand there will be some delay in showing the image compared to the actual image captures (three cycles). What I don't understand about running the algorithm is that there are certain levels of a getting-stuck phenomenon. It could be like the following:

  1. smoothly showing the results from r1, r2, r3, r4 and then getting stuck for 1s and then smoothly showing the results from r1-r4 and ...

OR

  1. smoothly showing the results from r2, r3, r4, r1and then getting stuck for 1s and then smoothly showing the results from r2-r1 and ...

it could be any sequence starting from either r1, r2, r3, r4. I don't understand what causes this stuck thing? Can anybody help analyze? Thanks.

Below is the snapshot of profiling:

enter image description here

Upvotes: 0

Views: 119

Answers (2)

abarnert
abarnert

Reputation: 366003

If you actually want to fire off the jobs as quickly as possible, and take the results as soon as they're available, without blocking the new jobs being fired, you have to stop interleaving them.

The simplest way to do this (assuming you want the results in order of task creation1) is probably to wait on a background thread, like this:

q = queue.Queue()

def handle():
    while True:
        res = q.get()
        output = res.get()
        showimage(output)

threading.Thread(target=handle)

while True:
    image = cap.read
    res = pool.apply_async(func, [image])
    q.put(res)

This exact design may not work—e.g., if showimage has to run on the main thread, you'll have to swap the two threads, and if cap.read also has to run on the main thread you'll need to manage multiple queue and make everything more complicated—but it should show the idea.


1. If you want the results in whatever order they finish, it will instead probably be simpler to switch from a multiprocessing.Pool to a concurrent.futures.ProcessPoolExecutor, because it's easier to wait on a group of futures than a group of AsyncResults. But there are other alternatives.

Upvotes: 0

muzzlator
muzzlator

Reputation: 742

Here's a simple example that explains the patterns you're seeing

Suppose you have 4 people with 4 empty glasses of water in front of them. Think of get() as "Finish drinking the water in your glass, i'm going to wait until you do before moving on". Think of apply_async as being "I'm going to fill up your glass, start drinking but I'm moving on".

So what happens:

count == 1
We fill person A's glass and they're drinking slowly
We wait for person B to finish their cup, it's already empty, we move on

count == 2
We fill person B's glass and they're drinking slowly
We wait for person C to finish their cup, it's already empty, we move on
...

count == 4
We fill person D's glass and they're drinking slowly
We wait for person A to finish their cup

OK so suppose A takes 30 seconds to finish their water but only takes us 5 seconds to do the steps above.

We're now going to be waiting 25 seconds for A to finish their drink before we move on. But with all that waiting time, person B, C and D also finished their drink, so once A is done, we zoom through the next 3 people, until we get back to A again.

Upvotes: 2

Related Questions