I am currently implementing a function to let me monitor the progress of a computation running in parallel (similar to the answers in Monitor doesn't work with ParallelTable). The goal is to have a function that can be wrapped around any parallel computation, to show the progress of it (e.g.: parallelProgress[ParallelTable[...]]
). Since something like that is nigh to impossible to implement for "any parallel computation", I settled for now to implementing it for the four big ones: ParallelTable
, ParallelMap
, ParallelDo
, ParallelSum
. After figuring out the basics, the implementation for ParallelTable
, ParallelDo
, ParallelSum
were rather straightforward (and almost identical). ParallelMap
however is giving me some problems, mainly due to the levelspec
parameter. The problem is to figure out the total number of function calls automatically and quick.
For example in
ParallelMap[f,Evaluate[Range[10]]]
the number of times f is called is easily calculated using Length
. For levelspec
different from {1}
you can calculate it using Dimensions
, as long as you have a rectangular array. However, in
ParallelMap[f,Evaluate[Range[Range[10]]],{2}]
the answer is not so straightforward. Here is what i did so far:
SetAttributes[parallelProgress, {HoldAll}]
parallelProgress[expr_] :=
...
Module[{...,max,...},
...
Count[holdexpr,
HoldPattern[
ParallelMap[
Alternatives[Function[par_, body_, attr_: {}], Function[body_],f_Symbol], mapExpr_, levelSpec_: {1},
OptionsPattern[ParallelMap]
] /; (
dim = Dimensions[mapExpr];
If[MatchQ[levelSpec, n_Integer /; (n >= 0 && n <= Length[dim])],
max += Times @@ Flatten[Table[dim[[i]], {i, 1, levelSpec}]],
If[MatchQ[levelSpec, {n_Integer} /; (n >= 0 && n <= Length[dim])],
max += Times @@ dim[[levelSpec[[1]]]],
If[MatchQ[levelSpec, {m_Integer,n_Integer} /; (n >= 0 && m >= 0 && m <= n && n <= Length[dim])],
max += Times @@ dim[[levelSpec[[1]] ;; levelSpec[[2]]]],
Map[max++&, mapExpr, levelSpec]]]]; True
)],
\[Infinity]];
...
]
I use patternmatching so that cases like parallelProgress[{ParallelMap[...],ParallelMap[...]}]
can also be handled. For all ParallelMap
instances found I first try to calculate the number of calls via Dimensions
. If that is not possible, I resort to Map[max++, mapExpr, levelSpec]
, which in my tests worked actually better than Length[Level[mapExpr, levelSpec]]
. However, this strikes me as being unnecessarily verbose. Is there a way to compute the number of function calls more easily and/or faster?
Edit:
Fixed a typo in the code.
Regarding the use of Length[Level[...]]
:
I compared this method with my simple Map[n++&,...]
:
Array[List, {100, 20, 1000}];
{2};
AbsoluteTiming[Module[{n = 0}, Map[n++ &, %%, %]; n]]
AbsoluteTiming[Length[Level[%%%, %%]]]
%[[1]]/%%[[1]]
With a variety of arrays and level specifications. While i didn't do a comprehensive study, my observations were as follows:
- With deeper levels
Level
is slightly faster (the lowest ratio I ever got from the code above was ~0.6) - With shallow levels and large lists
Map
is much faster (the highest ratio I ever got from the code above was ~100 !!)
but most importantly:
Level
seemed to crash (run out of memory, I guess) for smaller Lists, compared toMap
.
All in all this leads me to prefer Map[n++ &,...]
over Length[Level[...]]
even though the latter is more concise (which with Mathematica usually means faster).
Length@Level[mapSecondArgument, mapLevelspec]
from yourParallelMap[f, mapSecondArgument, mapLevelspec]
call would give you the number of elements to be mapped over, and therefore the number of function calls (unless the function to be mapped is recursive, in which case all bets are off...). Perhaps I am missing a more complex case though. $\endgroup$