# ParallelTable slowdown for a task involving ComponentMeasurements

I'm having trouble understanding a slowdown associated with code I'm running, which should be trivial to parallelize.

My code looks something like this:

DistributeDefinitions[Movie];

List =
ParallelTable[
ComponentMeasurements[
{
MorphologicalComponents[EdgeDetect[Movie[[frame]]], Method -> "ConvexHull"],
Movie[[frame]]
},
"IntensityCentroid"][[All, 2]],
{frame, 1, 100}];

This works, but takes about 14.265 seconds to run on 12 cores. However, changing "ParallelTable" to just "Table" reduces the running time to about 2.791. Changing {frame, 1, 100} to {frame, 1, 10} still yields the same result that "Table" runs about 5x as fast as "ParallelTable". The "Movie" data set is a few gigs in size, but shouldn't that not matter if I'm applying "DistributeDefinitions" to it?

For a self-contained example, we can use the "Mandrill" example data image in Mathematica 9:

Movie = Table[ExampleData[{"TestImage", "Mandrill"}], {x, 1, 5000}];
DistributeDefinitions[Movie];
ParallelTable[
ComponentMeasurements[
{
MorphologicalComponents[EdgeDetect[Movie[[frame]]], Method -> "ConvexHull"],
Movie[[frame]]
},
"IntensityCentroid"][[All, 2]],
{frame, 1, 20}];

Running "ParallelTable" here takes about 6.85 seconds of time on a 12 core machine. Running "Table" 7.24 seconds. However, changing the number of frames in the movie from 5,000 to 10,000

Movie = Table[ExampleData[{"TestImage", "Mandrill"}], {x, 1, 5000}];

to

Movie = Table[ExampleData[{"TestImage", "Mandrill"}], {x, 1, 10000}];

yields the same Table computation time of 7.26 seconds and increases the ParallelTable computation time to 9.71 seconds.

-

Breaking computation into smallest possible subunits

You should start to package your work load into small subunits to get the most out of ParallelTable.

Since Movie is in the same context it will get distributed automatically.

Let's define a function for your image manipulation:

compMeas[image_] :=
ComponentMeasurements[{MorphologicalComponents[EdgeDetect[image],
Method -> "ConvexHull"], image}, "IntensityCentroid"][[All, 2]]

Now the more complicated part. You need to measure what packaging method is best for your architecture. Parallelisation is always about measuring! You can't just expect to get a boost in calculation only by using the parallelised version.

I'm working here on a 4-core machine and the following options work fine on mine:

LaunchKernels[];

list = ParallelTable[compMeas[Movie[[frame]]], {frame, 1, 20},
Method -> "FinestGrained",
DistributedContexts -> None]; // AbsoluteTiming

==> 5.894273

Regular Table yields: 10.484547. So, twice as fast. Exactly what someone would expect.

Choosing "EvaluationsPerKernel"->2 or "ItemsPerEvaluation" -> 4 is another gain in speed, although marginal, but this may change if you increase the iteration range.

Btw. If you are finished with your parallel computation and you have changed the DistributedContext option for ParallelTable, you should restore its value to the default:

SetOptions[ParallelTable, DistributedContexts :> $DistributedContexts] Edit 1: Let me get more detailed about how to leverage the most out of ParallelTable. The problem, as you describe it, is not the ComponentMeasurements functionality. The problem is the work load. The work load in your case is Movies. Parallel algorithms work best, if you package the work load into small subunits. These subunits are getting distributed to all the available kernels who will evaluate the code you've defined inside your ParallelTable. Let's investigate that further. At first we start with a fresh kernel: Quit[]; Movie = Table[ExampleData[{"TestImage", "Mandrill"}], {x, 1, 20}]; and let's define our compMeas function: compMeas[image_] := Labeled[Framed[EdgeDetect@image],$KernelID]

with a fresh kernel we need to share Movie, so that the other kernels can access it:

SetSharedVariable[Movie];

ParallelTable[compMeas[Movie[[frame]]], {frame, 1, 10},
Method -> "FinestGrained", DistributedContexts -> None]

What we can see here, that although Movie was declared to be a shared variable, the whole calculation took place on the master kernel.

Let's change the DistributedContexts option to Automatic:

ParallelTable[compMeas[Movie[[frame]]], {frame, 1, 10},
Method -> "FinestGrained", DistributedContexts -> Automatic]

Now we can see, that not only Movie got distributed correctly but the definition of compMeas as well. You could've achieved exactly the same by setting compMeas to be distributed:

DistributeDefinitions[compMeas]
-
"You should start to package your work load into small subunits to get the most out of ParallelTable." What exactly did you mean by this comment? That I should define a function for my task as you illustrate? –  SqrtOfBlue Jul 6 '13 at 9:48
@SqrtOfBlue no. you should consider to package your workload (the frames of your movie) into small packages which get distributed among your kernels. that i defined the function was more to point out the ParallelTable options. –  Stefan Jul 6 '13 at 9:54
Thanks - when would I use DistributeDefinitions? It seems like you don't here, and everything is ok? –  SqrtOfBlue Jul 6 '13 at 9:56
@SqrtOfBlue by default, current context definitions are distributed automatically. if you have some definitions in another context you may set DistributedContexts to Automatic, which distributes automatically all contexts appearing in a parallel computation, or you define the specific context explicitly with DistributedContexts->{"blah`"}. –  Stefan Jul 6 '13 at 9:59
@SqrtOfBlue just as an advice. I'd avoid to use any Automatic in the beginnings unless you exactly know what to do and what options are best working for you. –  Stefan Jul 6 '13 at 10:00