Grid Computing - Maple Help

 Grid Computing

The Grid package contains methods for multiprocess parallel execution. Unlimited same-machine parallel execution is built-into Maple 2015. You can spawn as many parallel processes as you want without requiring any additional toolbox or licensing.

Maple 2015 makes it even easier to initiate parallel jobs with new commands that abstract away MPI-like message passing protocols. The result is a very simple and intuitive interface for running commands and dealing with data.

New Commands: Run, Set, Get, GetLastResult, Wait, WaitForFirst

$\mathrm{with}\left(\mathrm{Grid}\right);$

 $\left[{\mathrm{Barrier}}{,}{\mathrm{Get}}{,}{\mathrm{GetLastResult}}{,}{\mathrm{Interrupt}}{,}{\mathrm{Launch}}{,}{\mathrm{Map}}{,}{\mathrm{MyNode}}{,}{\mathrm{NumNodes}}{,}{\mathrm{Receive}}{,}{\mathrm{Run}}{,}{\mathrm{Send}}{,}{\mathrm{Seq}}{,}{\mathrm{Server}}{,}{\mathrm{Set}}{,}{\mathrm{Setup}}{,}{\mathrm{Status}}{,}{\mathrm{Wait}}{,}{\mathrm{WaitForFirst}}\right]$ (1.1)

The key new command, Grid:-Run, lets you spawn asynchronous jobs on individual nodes. This means:

 • no need to wait for the result
 • can be used with 1 or more nodes
 • when all nodes are used, Run implicitly waits, and returns individual node results
 • usually does not require Send and Receive

Example: Background Jobs

This example shows how to run two jobs in the background. The assignto option allows you to capture the results in the local session. It is important to wait for the jobs to finish before using the results or starting new jobs on the same nodes.

 > $\mathrm{Grid}:-\mathrm{Run}\left(0,\mathrm{Optimization}:-\mathrm{NLPSolve},\left[\frac{\mathrm{sin}\left(x\right)}{x},x=1..30\right],'\mathrm{assignto}'='\mathrm{ans0}'\right)$
 > $\mathrm{Grid}:-\mathrm{Run}\left(1,\mathrm{Optimization}:-\mathrm{NLPSolve},\left[{x}^{3}+2xy-2{y}^{2},x=-10..10,y=-10..10,\mathrm{initialpoint}=\left\{x=3,y=4\right\},'\mathrm{maximize}'\right],'\mathrm{assignto}'='\mathrm{ans1}'\right)$
 > $\mathrm{Grid}:-\mathrm{Wait}\left(\right)$
 > $\mathrm{ans0}$
 $\left[{-}{0.0424796169776126}{,}\left[{x}{=}{23.5194525023235}\right]\right]$ (1.1.1)
 > $\mathrm{ans1}$
 $\left[{1050.}{,}\left[{x}{=}{10.}{,}{y}{=}{5.}\right]\right]$ (1.1.2)



These same commands could have been run in serial as shown later. Using the Run command with two computations as above lets you run both commands at the same time. The first command does not need to finish before the second one can start. Running two commands in parallel can cut your execution time in half. Run many jobs to achieve even greater performance improvements compared to sequential jobs.



 >
 $\left[{-}{0.0424796169776126}{,}\left[{x}{=}{23.5194525023235}\right]\right]$ (1.1.3)
 >
 $\left[{1050.}{,}\left[{x}{=}{10.}{,}{y}{=}{5.}\right]\right]$ (1.1.4)

Example: Results from All Nodes

This example shows results from multi-node computations can be returned in an array. Note that the Grid:-Set command is used to assign values from the current session into the worker nodes. In this case, the work procedure is defined locally, and therefore not known to the parallel processes until the Set command is applied.

 >
 > $\mathrm{Grid}:-\mathrm{Set}\left(\mathrm{work}\right);$
 >
 ${R}{:=}{\mathrm{Array}}{}\left({0}{..}{3}{,}\left\{{0}{=}{"node 0 computed 9"}{,}{1}{=}{"node 1 computed 10"}{,}{2}{=}{"node 2 computed 5"}{,}{3}{=}{"node 3 computed 8"}\right\}\right)$ (1.2.1)
 > $R\left[0\right];$
 ${"node 0 computed 9"}$ (1.2.2)
 > $R\left[1\right];$
 ${"node 1 computed 10"}$ (1.2.3)
 > $R\left[2\right];$
 ${"node 2 computed 5"}$ (1.2.4)
 > $R\left[3\right];$
 ${"node 3 computed 8"}$ (1.2.5)

A 4-core machine will automatically spawn 4 jobs when no target node is specified as the first parameter to Run. All results will be returned in an array with result at the corresponding node number.

Example: Competition - First-to-Finish Wins

This example will run 4 different jobs and only wait for the first one to finish; then will terminate the others. As mentioned earlier, note the use of Grid:-Set to define the delay procedure on the remote nodes.

 >
 >
 >
 >
 >
 > $n:=\mathrm{Grid}:-\mathrm{WaitForFirst}\left(\right)$
 ${n}{:=}{2}$ (1.3.1)
 > $\mathrm{Grid}:-\mathrm{Interrupt}\left(\right)$
 > $\mathrm{Grid}:-\mathrm{Wait}\left(\right)$
 > $\mathrm{Grid}:-\mathrm{GetLastResult}\left(n\right)$
 ${"result of method 2"}$ (1.3.2)

The Grid:-WaitForFirst command will return the node number of the first node to finish computing. The Grid:-GetLastResult can then be used to fetch whatever value was last computed on that node. If that computed result had a name, the Get command could also be used to fetch the value.



Improvements to Grid:-Map and Grid:-Seq

Grid:-Map and Grid:-Seq have been re-implemented using Grid:-Run. This means:

 • better division of work

In this example, the work can be unevenly divided. The sequence of heavy computations at the end of the list is may require an adjustment to the default tasksize

 >
 ${\mathrm{data}}{:=}\left[{5}{,}{25}{,}{125}{,}{625}{,}{3125}{,}{15625}{,}{78125}{,}{390625}{,}{1953125}{,}{9765625}{,}{9765625}{,}{9765625}\right]$ (2.1.1)
 >
 node 2 computing 78125 node 1 computing 625 node 3 computing 9765625 node 0 computing 5 node 0 computing 25 node 1 computing 3125 node 0 computing 125 node 1 computing 15625 node 2 computing 390625 node 2 computing 1953125 node 3 computing 9765625 node 3 computing 9765625
 ${2.312}$ (2.1.2)



With only a few numbers in the computation, the default is to divide the operation into equal portions. Node 0 gets the first three data points, node 1 gets the next three, and so on. Because, in this case, the work is unevenly distributed, node 3 ends up computing the three hardest cases and becomes the bottleneck.

To optimize the timing on this kind of example, you can set a smaller tasksize. With tasksize=1, the algorithm will split the data into chunks of 1 element (instead of 3-elements in the previous example). There are 12 tasks but only 4 compute nodes. Each node will finish one task before asking for another.

 >
 node 0 computing 5 node 1 computing 25 node 3 computing 625 node 2 computing 125 node 0 computing 3125 node 1 computing 15625 node 2 computing 78125 node 3 computing 390625 node 0 computing 1953125 node 1 computing 9765625 node 2 computing 9765625 node 3 computing 9765625
 ${1.609}$ (2.1.3)



time[current]

A new option to the time() command allows fine-grained, sub-second measurement of the current time, which can be useful when comparing execution in different processes.

This example displays a message including the current clock time before, during, and after running jobs on all nodes.

 > show_time := proc( msg := "" )     local t := time[current]();     printf("node=%d current-time=%s.%d: %s\n",        Grid:-MyNode(),        StringTools:-FormatTime("%I:%M:%S",timestamp=trunc(t)),           trunc(10^6*(t - trunc(t))),        msg); end proc:
 > $\mathrm{Grid}:-\mathrm{Set}\left(\mathrm{show_time}\right)$
 > $\mathrm{show_time}\left("initialize"\right)\phantom{\rule[-0.0ex]{0.0em}{0.0ex}}\phantom{\rule[-0.0ex]{0.0em}{0.0ex}}\mathrm{Grid}:-\mathrm{Run}\left(\mathrm{show_time},\left["worker"\right],'\mathrm{wait}'=\mathrm{true}\right):\phantom{\rule[-0.0ex]{0.0em}{0.0ex}}\phantom{\rule[-0.0ex]{0.0em}{0.0ex}}\mathrm{show_time}\left("done"\right)$
 node=0 current-time=02:32:11.781000: initialize node=0 current-time=02:32:11.781000: worker node=1 current-time=02:32:11.781000: worker node=2 current-time=02:32:11.781000: worker node=3 current-time=02:32:11.797000: worker node=0 current-time=02:32:11.797000: done