# Monitor doesn't work with ParallelTable

I can't monitor ParallelTable:

Monitor[ParallelTable[Pause[3]; i, {i, 1, 10}], i]


just displays i until it is finished.

Do you guys know of alternatives?

-
Can you expand your question with what happens when you try? Are error messages generated? Does it just not do anything? –  rcollyer Feb 9 '12 at 16:32
BTW the reason is that ParallelTable might send values to process to subkernels in batches (depending on the Method setting), so the table iterator variable does not get values sequentially as in the case of Table. –  Szabolcs Feb 9 '12 at 16:34
@Szabolcs I think his question relates to the fact that Monitor[ParallelTable[expr, {i, 1, 10}], i] just displays i until it's finished –  acl Feb 9 '12 at 16:36
@acl I got that but the point is that it is not immediately clear what the question is asking unless one has already encountered the problem. I have seen the problem, but I have not taken the time to work out a practical solution yet (one that does not significantly hurt performance I mean---it's possible to send progress back to the main kernel, but the communication cost can be significant) –  Szabolcs Feb 9 '12 at 16:38
@Szabolcs Actually I was more asking you how ParallelTable possibly sending batches of expressions to each kernel explains this (as I don't understand) rather than clarifying his answer... –  acl Feb 9 '12 at 16:39

One way is to set a shared variable that would be assigned to an iterator variable, and monitor that:

SetSharedVariable[j]
Monitor[
ParallelTable[j = n;Length[FactorInteger[2^n - 1]], {n, 50, 300}],
j
]


This may make sense if the computation for each i is rather intensive, so that the overhead of communication with the main kernel is negligible. Note also that the results you see are not generally in sequential order, since they depend on how ParallelTable schedules the computations to available kernels. As to the original example, here is a modified version,

SetSharedVariable[j]
Monitor[ParallelTable[Pause[RandomReal[{0.5, 4.}]];j = i, {i, 1, 10}], j]


where the intervals to pause are random, so that not all kernels finish computing at the same time.

EDIT

As mentioned by @Szabolcs in the comments,

• You could use j++ in place of j=i, if you are mostly interested in the overall progress
• One should be aware of what type of communication overhead this induces.

Here is one way to find out:

j = 0;
First@AbsoluteTiming[ParallelTable[j++, {i, 1, 1000}];]/1000


which returns 0.0028 on my machine.

-
It would be more useful to increment j every time a value is computed. This way one could monitor how many values have already been computed out of the total. The order of computations can be just about anything, so looking at the "current value" of n is not very informative. –  Szabolcs Feb 9 '12 at 16:58
Accessing a shared variable took ~10 ms last time I checked. To have a feel for it, this could introduce a 1 second overhead per 100 elements computed. But don't take my word for it, benchmark! –  Szabolcs Feb 9 '12 at 16:59
@Szabolcs Agree that using j++ would probably make more sense, if one is interested in the overall progress only. As to the overhead - yes, sure, that's why I mentioned that it may make sense when the computation for each single i is intensive. –  Leonid Shifrin Feb 9 '12 at 17:01
When you're calculating a long list you could decrease the overhead by incrementing j only during some of the iterations (for example when Mod[i,10]==0 or so). –  Heike Feb 9 '12 at 17:09
@Heike Yes, sure, although it may be tricky depending on how the tasks are scheduled to the parallel kernels - they need not at all be sequential. But I wasn't concerned with the exact scheme, since this depends on the particular problem - I was just illustrating a general idea. –  Leonid Shifrin Feb 9 '12 at 17:11

Modifying some ideas suggested here, and a suggestions from Leonid in the comments:

SetAttributes[monitorParallelTable,HoldAll]
monitorParallelTable[expr_,iter__List,updatethreshold_]:=
Module[{counter=1,thresh=updatethreshold},
SetSharedVariable[counter];
ParallelEvaluate[localcounter=1;];
Monitor[
ParallelTable[
If[localcounter>=thresh,counter=counter+localcounter;localcounter=1,
localcounter++];expr,iter],
counter]
]


Basically, each kernel keeps a working tally of the number of elements it's solved, which dumps to a shared counter once it crosses an adjustable threshold. For example:

monitorParallelTable[Pause[RandomReal[{0.5, 4.}]]; n, {n, 5000}, 2]


Also, this should work for nested Table, e.g.

monitorParallelTable[Pause[RandomReal[{0.5, 4.}]]; n, {n, 5000}, {m,300}, 2]


A quick check on AbsoluteTiming shows the performance hit as a function of the threshold value:

Edit Not entirely sure why, but the counter that is monitored goes from 1 to (Maximum Iterations)/(number of kernels) rather than from 1 to (Maximum iterations)

-
There's probably some way to work this out for an arbitrary Parallel computation, but I'm not quite sure how to do that (i.e. somehow inject the If statement into arbitrary parallel code). –  Eli Lansey Feb 9 '12 at 20:05
The problem seems to be (from the look at it, I did not test) that your expr evaluates before you pass it to monitorParallelTable, so I suspect that both your methods of pausing are equally ineffective simply because the CompoundExpression evaluates too early and you actually pass just n (that is, if n did not have a global value). I think, you need some Hold* - attribute here, perhaps HoldAll, to make this work. –  Leonid Shifrin Feb 9 '12 at 21:19
The strange thing is, sometimes Pause works, sometimes it's as you described. First a pause, then a rapid evaluation. Other times it seems to run on the parallel kernels. I've never really quite worked out how the Hold business functions. Where would I put it? –  Eli Lansey Feb 9 '12 at 21:24
Just add a line SetAttributes[monitorParallelTable,HoldAll] before your main code. As to Hold*-attributes, search on Stack Overflow Mathematica tag, there were a couple of good threads on them there. Here is one I remember about: stackoverflow.com/questions/4856177/… –  Leonid Shifrin Feb 9 '12 at 21:45
@Valerio Just added a small tweak to ensure all the table variations pass through the function. –  Eli Lansey Feb 14 '12 at 14:45

This is my final code for implementing a long calculation (demonstrated here over a plane of values) to ensure that all processors are being used and code to monitor the progress, with estimates of time remaining. The last line exports the data to a location specified in that line, so that you can easily come back to it and use it later.

(* First define your function using this format f[x_,z_], presumably \
you may have many function definitions that build off of one another, \
this is where the physics goes *)
f[x_, z_] := N[Sin[ x z]]
(* Now define the boundaries of the plane that you wish to calculate \
values over *)
xmin = 1;
xmax = 2 \[Pi];
zmin = 1;
zmax = 8;
(* Now define how many points you wish to calculate the function \
along each axis, note that the total number of calculatons will be \
xstep*ystep.
I reccomend running this prelminarily with a small number of points \
(ie 10 x 10) to determine the average time per point, so that you may \
predict how many points will take a given amout of time *)
xstep = 100;
zstep = 100;
(* You shouldn't have to touch any of the code from 1here1 to, unless \
you want to run several of these statement and are worried about the \
names of the various tables, in which case rename tab1, but don't \
forget to change the name in the export command *)
timestart = AbsoluteTime[];
counter = 0;
SetSharedVariable[counter];
PrintTemporary[Dynamic[
"Percent Completed: " <>
ToString[N[(counter*100)/((xstep + 1) (zstep + 1))]]
<> "% \nTime elapsed: " <> ToString[AbsoluteTime[] - timestart] <>
" s \nEstimated Time remaining: " <>
ToString[(((xstep + 1) (zstep + 1) - counter) (AbsoluteTime[] -
timestart))/counter]
]];
tab1 = ParallelTable[counter++;
N[f[x, z]], {x, xmin, xmax, N[Abs[xmin - xmax]/xstep]}, {z, zmin,
zmax, N[Abs[zmin - zmax]/zstep]}];
timeend = AbsoluteTime[];
Print["Total calculation time: ", timeend - timestart, " s"]
Print["Average time per data point: ", (
timeend - timestart)/((xstep + 1) (zstep + 1)), " s"]
(* 1here1 *)
(* Change the location and file name, I suggest something that is \
meaningful and you will be able to remember easily *)
Export["C:\\Users\\Ben\\Documents\\Code for group\\example.dat", tab1]

-