1
$\begingroup$

I have a nested Do and I need to parallelize the outer Do. I replaced the Do by ParalleleDo in the outer Do but with this I don't have out. Why?

The Do is some like:

do = {}; 
   Do[Print[i]; 
         data = DeleteCases[Reap[Do[Sow[1 + i], {i, 3}]], Null] *i;
            do = Append[do, data], {i, 4}];

The out of this is:

 do = {{{{2,3,4}}},{{{4,6,8}}},{{{6,9,12}}},{{{8,12,16}}}}

Then, I need that each list be process by one different kernel, I put ParallelDo:

 do = {}; 
   ParallelDo[Print[i]; 
         data = DeleteCases[Reap[Do[Sow[1 + i], {i, 3}]], Null] *i;
            do = Append[do, data], {i, 4}];

And the out is:

 do = {}

Why?

Really my Dois more complex but this summarizes why I need to only parallelize the outer Do.

$\endgroup$
  • 1
    $\begingroup$ Have you tried using ParallelTable ? ParallelTable[ DeleteCases[Reap[Do[Sow[1 + i], {i, 3}]], Null]*i, {i, 3}] $\endgroup$ – Awkward Panda May 5 '18 at 21:57
  • $\begingroup$ It sounds like you're asking two questions - "I don't have out. Why?" and how to parallelize your particular construct, is that right? $\endgroup$ – Carl Lange May 5 '18 at 21:59
  • $\begingroup$ Using a FrontEnd-dependend command like Print that often in parallelized code won't improve performance. Actually, using Print slows down everything. You are probably looking for ParallelTable. $\endgroup$ – Henrik Schumacher May 6 '18 at 7:16
1
$\begingroup$

Your problem is that you have 2 different do variables: one the first do before the ParallelDo, and another within the parallel do, which is the declared in each kernel, and lives only in there. If you want to have such a global variable you would need to set it as shared variable. But even if you do it that way, you will have a possible race condition and only some of the kernels will actually update the right variable. You can also set a shared function to update only a downvalue of do, and that may work:

do[_] = {}
SetSharedFunction[do]
ParallelDo[Print[i];
  data = DeleteCases[Reap[Do[Sow[1 + i], {i, 3}]], Null]*i;
  Print[data];
  do[$KernelID] = Append[do[$KernelID], data], {i, 4}];

And you can join the results in different ways, like doing another Do, or better a Table or even better a ParallelTable:

DeleteCases[ParallelTable[do[$KernelID], {Kernels[]}], {}]

But of course, all that is just the wrong way to do the parallel task because usually you don't want to use Do but Table, and in this case ParallelTable may be much better in the first place:

ParallelTable[Print[i];
 Table[1 + j, {j, 3}]*i, {i, 4}]

I also didn't see the need of that Reap/Sow structure within a Do, that's again what Table does (and simpler because you won't need DeleteCases), and also try to avoid using the same variable for nested structures, that may lead to hard to debug issues.

That returns the same result as your first Do with the only difference of list nesting, but probably that shouldn't be an issue, and if it is, it is just as easy as wrapping your results. (And I think that Print should go away once you see how that's working).

$\endgroup$
  • 2
    $\begingroup$ I would add the following rule of thumb: when you parallize, don't use shared values to aggregate results. Every time a shared symbol is updated, the values have to be distributed to all the kernels and this leads to communication overhead. Instead, you simply want each kernel to do its own thing and then come back to you with a big block of results that can be mashed together with the results from the other kernels. This is basically what ParallelTable does for constructing large lists and it's the best way to leverage parallelization. $\endgroup$ – Sjoerd Smit May 6 '18 at 13:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.