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
Improvements to Grid:-Map and Grid:-Seq
time[current]
withGrid;
Barrier,Get,GetLastResult,Interrupt,Launch,Map,MyNode,NumNodes,Receive,Run,Send,Seq,Server,Set,Setup,Status,Wait,WaitForFirst
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.
Grid:-Run⁡0,Optimization:-NLPSolve,sin⁡xx,x=1..30,'assignto'='ans0'
Grid:-Run⁡1,Optimization:-NLPSolve,x3+2⁢x⁢y−2⁢y2,x=−10..10,y=−10..10,initialpoint=x=3,y=4,'maximize','assignto'='ans1'
Grid:-Wait⁡
ans0
−0.0424796169776126,x=23.5194525023235
ans1
1050.,x=10.,y=5.
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.
Optimization:-NLPSolve sin⁡xx,x=1..30;
Optimization:-NLPSolve x3+2⁢x⁢y−2⁢y2,x=−10..10,y=−10..10,initialpoint=x=3,y=4,'maximize' ;
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.
work ≔ proc randomize; catnode , Grid:-MyNode, computed , rand1..10 ; end:
Grid:-Setwork;
R ≔ Grid:-Runwork
R0;
node 0 computed 9
R1;
node 1 computed 10
R2;
node 2 computed 5
R3;
node 3 computed 8
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.
method0 ≔ proc Threads:-Sleep15: result of method 0; end: Grid:-Set0,method0;
method1 ≔ proc Threads:-Sleep13: result of method 1; end: Grid:-Set1,method1;
method2 ≔ proc Threads:-Sleep3: result of method 2; end: Grid:-Set2,method2;
method3 ≔ proc Threads:-Sleep9: result of method 3; end: Grid:-Set3,method3;
Grid:-Run0,method0;Grid:-Run1,method1;Grid:-Run2,method2;Grid:-Run3,method3;
n:=Grid:-WaitForFirst⁡
n:=2
Grid:-Interrupt⁡
Grid:-GetLastResult⁡n
result of method 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.
Grid:-Map and Grid:-Seq have been re-implemented using Grid:-Run. This means:
less overhead spawning processes
better division of work
optional tasksize parameter
tasksize
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
data ≔ seq5^mini,10,i=1..12;
data:=5,25,125,625,3125,15625,78125,390625,1953125,9765625,9765625,9765625
timereal Grid:-Mapprocn printfnode %d computing %d\n,Grid:-MyNode,n; addi,i=1..n; end proc, data ;
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
2.312
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.
timereal Grid:-Maptasksize=1procn printfnode %d computing %d\n,Grid:-MyNode,n; addi,i=1..n; end proc, data ;
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
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:
Grid:-Set⁡show_time
show_timeinitializeGrid:-Runshow_time,worker,'wait'=true:show_timedone
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
Download Help Document